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: (47 commits)
  Driver core: Don't call put methods while holding a spinlock
  Driver core: Remove unneeded routines from driver core
  Driver core: Fix potential deadlock in driver core
  PCI: enable driver multi-threaded probe
  Driver Core: add ability for drivers to do a threaded probe
  sysfs: add proper sysfs_init() prototype
  drivers/base: check errors
  drivers/base: Platform notify needs to occur before drivers attach to the device
  v4l-dev2: handle __must_check
  add CONFIG_ENABLE_MUST_CHECK
  add __must_check to device management code
  Driver core: fixed add_bind_files() definition
  Driver core: fix comments in drivers/base/power/resume.c
  sysfs_remove_bin_file: no return value, dump_stack on error
  kobject: must_check fixes
  Driver core: add ability for devices to create and remove bin files
  Class: add support for class interfaces for devices
  Driver core: create devices/virtual/ tree
  Driver core: add device_rename function
  Driver core: add ability for classes to handle devices properly
  ...
This commit is contained in:
Linus Torvalds
2006-09-26 11:49:46 -07:00
55 changed files with 1624 additions and 542 deletions
@@ -1,13 +1,12 @@
What: devfs What: devfs
Date: July 2005 Date: July 2005 (scheduled), finally removed in kernel v2.6.18
Contact: Greg Kroah-Hartman <gregkh@suse.de> Contact: Greg Kroah-Hartman <gregkh@suse.de>
Description: Description:
devfs has been unmaintained for a number of years, has unfixable devfs has been unmaintained for a number of years, has unfixable
races, contains a naming policy within the kernel that is races, contains a naming policy within the kernel that is
against the LSB, and can be replaced by using udev. against the LSB, and can be replaced by using udev.
The files fs/devfs/*, include/linux/devfs_fs*.h will be removed, The files fs/devfs/*, include/linux/devfs_fs*.h were removed,
along with the the assorted devfs function calls throughout the along with the the assorted devfs function calls throughout the
kernel tree. kernel tree.
Users: Users:
+88
View File
@@ -0,0 +1,88 @@
What: /sys/power/
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power directory will contain files that will
provide a unified interface to the power management
subsystem.
What: /sys/power/state
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/state file controls the system power state.
Reading from this file returns what states are supported,
which is hard-coded to 'standby' (Power-On Suspend), 'mem'
(Suspend-to-RAM), and 'disk' (Suspend-to-Disk).
Writing to this file one of these strings causes the system to
transition into that state. Please see the file
Documentation/power/states.txt for a description of each of
these states.
What: /sys/power/disk
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/disk file controls the operating mode of the
suspend-to-disk mechanism. Reading from this file returns
the name of the method by which the system will be put to
sleep on the next suspend. There are four methods supported:
'firmware' - means that the memory image will be saved to disk
by some firmware, in which case we also assume that the
firmware will handle the system suspend.
'platform' - the memory image will be saved by the kernel and
the system will be put to sleep by the platform driver (e.g.
ACPI or other PM registers).
'shutdown' - the memory image will be saved by the kernel and
the system will be powered off.
'reboot' - the memory image will be saved by the kernel and
the system will be rebooted.
The suspend-to-disk method may be chosen by writing to this
file one of the accepted strings:
'firmware'
'platform'
'shutdown'
'reboot'
It will only change to 'firmware' or 'platform' if the system
supports that.
What: /sys/power/image_size
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/image_size file controls the size of the image
created by the suspend-to-disk mechanism. It can be written a
string representing a non-negative integer that will be used
as an upper limit of the image size, in bytes. The kernel's
suspend-to-disk code will do its best to ensure the image size
will not exceed this number. However, if it turns out to be
impossible, the kernel will try to suspend anyway using the
smallest image possible. In particular, if "0" is written to
this file, the suspend image will be as small as possible.
Reading from this file will display the current image size
limit, which is set to 500 MB by default.
What: /sys/power/pm_trace
Date: August 2006
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/power/pm_trace file controls the code which saves the
last PM event point in the RTC across reboots, so that you can
debug a machine that just hangs during suspend (or more
commonly, during resume). Namely, the RTC is only used to save
the last PM event point if this file contains '1'. Initially
it contains '0' which may be changed to '1' by writing a
string representing a nonzero integer into it.
To use this debugging feature you should attempt to suspend
the machine, then reboot it and run
dmesg -s 1000000 | grep 'hash matches'
CAUTION: Using it will cause your machine's real-time (CMOS)
clock to be set to a random invalid time after a resume.
@@ -6,6 +6,21 @@ be removed from this file.
--------------------------- ---------------------------
What: /sys/devices/.../power/state
dev->power.power_state
dpm_runtime_{suspend,resume)()
When: July 2007
Why: Broken design for runtime control over driver power states, confusing
driver-internal runtime power management with: mechanisms to support
system-wide sleep state transitions; event codes that distinguish
different phases of swsusp "sleep" transitions; and userspace policy
inputs. This framework was never widely used, and most attempts to
use it were broken. Drivers should instead be exposing domain-specific
interfaces either to kernel or to userspace.
Who: Pavel Machek <pavel@suse.cz>
---------------------------
What: RAW driver (CONFIG_RAW_DRIVER) What: RAW driver (CONFIG_RAW_DRIVER)
When: December 2005 When: December 2005
Why: declared obsolete since kernel 2.6.3 Why: declared obsolete since kernel 2.6.3
@@ -294,3 +309,15 @@ Why: The frame diverter is included in most distribution kernels, but is
It is not clear if anyone is still using it. It is not clear if anyone is still using it.
Who: Stephen Hemminger <shemminger@osdl.org> Who: Stephen Hemminger <shemminger@osdl.org>
---------------------------
What: PHYSDEVPATH, PHYSDEVBUS, PHYSDEVDRIVER in the uevent environment
When: Oktober 2008
Why: The stacking of class devices makes these values misleading and
inconsistent.
Class devices should not carry any of these properties, and bus
devices have SUBSYTEM and DRIVER as a replacement.
Who: Kay Sievers <kay.sievers@suse.de>
---------------------------
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
extern int attribute_container_init(void); extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev); extern int bus_add_device(struct device * dev);
extern void bus_attach_device(struct device * dev); extern int bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev); extern void bus_remove_device(struct device * dev);
extern struct bus_type *get_bus(struct bus_type * bus); extern struct bus_type *get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus);
+79 -54
View File
@@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
if (bus) { if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev); error = device_add_attrs(bus, dev);
if (!error) { if (error)
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); goto out;
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem"); error = sysfs_create_link(&bus->devices.kobj,
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus"); &dev->kobj, dev->bus_id);
} if (error)
goto out;
error = sysfs_create_link(&dev->kobj,
&dev->bus->subsys.kset.kobj, "subsystem");
if (error)
goto out;
error = sysfs_create_link(&dev->kobj,
&dev->bus->subsys.kset.kobj, "bus");
} }
out:
return error; return error;
} }
@@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
* bus_attach_device - add device to bus * bus_attach_device - add device to bus
* @dev: device tried to attach to a driver * @dev: device tried to attach to a driver
* *
* - Add device to bus's list of devices.
* - Try to attach to driver. * - Try to attach to driver.
*/ */
void bus_attach_device(struct device * dev) int bus_attach_device(struct device * dev)
{ {
struct bus_type * bus = dev->bus; struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) { if (bus) {
device_attach(dev); dev->is_registered = 1;
klist_add_tail(&dev->knode_bus, &bus->klist_devices); ret = device_attach(dev);
if (ret >= 0) {
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
ret = 0;
} else
dev->is_registered = 0;
} }
return ret;
} }
/** /**
@@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev)
sysfs_remove_link(&dev->kobj, "bus"); sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id); sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev); device_remove_attrs(dev->bus, dev);
klist_remove(&dev->knode_bus); dev->is_registered = 0;
klist_del(&dev->knode_bus);
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id); pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
device_release_driver(dev); device_release_driver(dev);
put_bus(dev->bus); put_bus(dev->bus);
@@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
* Thanks to drivers making their tables __devinit, we can't allow manual * Thanks to drivers making their tables __devinit, we can't allow manual
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled. * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
*/ */
static void add_bind_files(struct device_driver *drv) static int __must_check add_bind_files(struct device_driver *drv)
{ {
driver_create_file(drv, &driver_attr_unbind); int ret;
driver_create_file(drv, &driver_attr_bind);
ret = driver_create_file(drv, &driver_attr_unbind);
if (ret == 0) {
ret = driver_create_file(drv, &driver_attr_bind);
if (ret)
driver_remove_file(drv, &driver_attr_unbind);
}
return ret;
} }
static void remove_bind_files(struct device_driver *drv) static void remove_bind_files(struct device_driver *drv)
@@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_unbind); driver_remove_file(drv, &driver_attr_unbind);
} }
#else #else
static inline void add_bind_files(struct device_driver *drv) {} static inline int add_bind_files(struct device_driver *drv) { return 0; }
static inline void remove_bind_files(struct device_driver *drv) {} static inline void remove_bind_files(struct device_driver *drv) {}
#endif #endif
@@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
* @drv: driver. * @drv: driver.
* *
*/ */
int bus_add_driver(struct device_driver * drv) int bus_add_driver(struct device_driver *drv)
{ {
struct bus_type * bus = get_bus(drv->bus); struct bus_type * bus = get_bus(drv->bus);
int error = 0; int error = 0;
@@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
if (bus) { if (bus) {
pr_debug("bus %s: add driver %s\n", bus->name, drv->name); pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error) { if (error)
put_bus(bus); goto out_put_bus;
return error;
}
drv->kobj.kset = &bus->drivers; drv->kobj.kset = &bus->drivers;
if ((error = kobject_register(&drv->kobj))) { if ((error = kobject_register(&drv->kobj)))
put_bus(bus); goto out_put_bus;
return error;
}
driver_attach(drv); error = driver_attach(drv);
if (error)
goto out_unregister;
klist_add_tail(&drv->knode_bus, &bus->klist_drivers); klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv); module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv); error = driver_add_attrs(bus, drv);
add_bind_files(drv); if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
} }
return error; return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
put_bus(bus);
return error;
} }
/** /**
* bus_remove_driver - delete driver from bus's knowledge. * bus_remove_driver - delete driver from bus's knowledge.
* @drv: driver. * @drv: driver.
@@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
/* Helper for bus_rescan_devices's iter */ /* Helper for bus_rescan_devices's iter */
static int bus_rescan_devices_helper(struct device *dev, void *data) static int __must_check bus_rescan_devices_helper(struct device *dev,
void *data)
{ {
int ret = 0;
if (!dev->driver) { if (!dev->driver) {
if (dev->parent) /* Needed for USB */ if (dev->parent) /* Needed for USB */
down(&dev->parent->sem); down(&dev->parent->sem);
device_attach(dev); ret = device_attach(dev);
if (dev->parent) if (dev->parent)
up(&dev->parent->sem); up(&dev->parent->sem);
if (ret > 0)
ret = 0;
} }
return 0; return ret < 0 ? ret : 0;
} }
/** /**
@@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data)
* attached and rescan it against existing drivers to see if it matches * attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices. * any by calling device_attach() for the unbound devices.
*/ */
void bus_rescan_devices(struct bus_type * bus) int bus_rescan_devices(struct bus_type * bus)
{ {
bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper); return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
} }
/** /**
@@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus)
* to use if probing criteria changed during a devices lifetime and * to use if probing criteria changed during a devices lifetime and
* driver attachment should change accordingly. * driver attachment should change accordingly.
*/ */
void device_reprobe(struct device *dev) int device_reprobe(struct device *dev)
{ {
if (dev->driver) { if (dev->driver) {
if (dev->parent) /* Needed for USB */ if (dev->parent) /* Needed for USB */
@@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
if (dev->parent) if (dev->parent)
up(&dev->parent->sem); up(&dev->parent->sem);
} }
return bus_rescan_devices_helper(dev, NULL);
bus_rescan_devices_helper(dev, NULL);
} }
EXPORT_SYMBOL_GPL(device_reprobe); EXPORT_SYMBOL_GPL(device_reprobe);
struct bus_type * get_bus(struct bus_type * bus) struct bus_type *get_bus(struct bus_type *bus)
{ {
return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL; return bus ? container_of(subsys_get(&bus->subsys),
struct bus_type, subsys) : NULL;
} }
void put_bus(struct bus_type * bus) void put_bus(struct bus_type * bus)
@@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
put_device(dev); put_device(dev);
} }
static void klist_drivers_get(struct klist_node *n)
{
struct device_driver *drv = container_of(n, struct device_driver,
knode_bus);
get_driver(drv);
}
static void klist_drivers_put(struct klist_node *n)
{
struct device_driver *drv = container_of(n, struct device_driver,
knode_bus);
put_driver(drv);
}
/** /**
* bus_register - register a bus with the system. * bus_register - register a bus with the system.
* @bus: bus. * @bus: bus.
@@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
goto bus_drivers_fail; goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put); klist_init(&bus->klist_drivers, NULL, NULL);
bus_add_attrs(bus); bus_add_attrs(bus);
pr_debug("bus type '%s' registered\n", bus->name); pr_debug("bus type '%s' registered\n", bus->name);
+31 -3
View File
@@ -19,6 +19,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "base.h" #include "base.h"
extern struct subsystem devices_subsys;
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) #define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj) #define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
@@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
* Note, the pointer created here is to be destroyed when finished by * Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy(). * making a call to class_destroy().
*/ */
struct class *class_create(struct module *owner, char *name) struct class *class_create(struct module *owner, const char *name)
{ {
struct class *cls; struct class *cls;
int retval; int retval;
@@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id); pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (class_dev->dev) { if (class_dev->dev) {
/* add physical device, backing this device */ /* add device, backing this class device (deprecated) */
struct device *dev = class_dev->dev; struct device *dev = class_dev->dev;
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL); char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
@@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev)
struct class_device *class_device_create(struct class *cls, struct class_device *class_device_create(struct class *cls,
struct class_device *parent, struct class_device *parent,
dev_t devt, dev_t devt,
struct device *device, char *fmt, ...) struct device *device,
const char *fmt, ...)
{ {
va_list args; va_list args;
struct class_device *class_dev = NULL; struct class_device *class_dev = NULL;
@@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
{ {
struct class *parent; struct class *parent;
struct class_device *class_dev; struct class_device *class_dev;
struct device *dev;
if (!class_intf || !class_intf->class) if (!class_intf || !class_intf->class)
return -ENODEV; return -ENODEV;
@@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node) list_for_each_entry(class_dev, &parent->children, node)
class_intf->add(class_dev, class_intf); class_intf->add(class_dev, class_intf);
} }
if (class_intf->add_dev) {
list_for_each_entry(dev, &parent->devices, node)
class_intf->add_dev(dev, class_intf);
}
up(&parent->sem); up(&parent->sem);
return 0; return 0;
@@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
{ {
struct class * parent = class_intf->class; struct class * parent = class_intf->class;
struct class_device *class_dev; struct class_device *class_dev;
struct device *dev;
if (!parent) if (!parent)
return; return;
@@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node) list_for_each_entry(class_dev, &parent->children, node)
class_intf->remove(class_dev, class_intf); class_intf->remove(class_dev, class_intf);
} }
if (class_intf->remove_dev) {
list_for_each_entry(dev, &parent->devices, node)
class_intf->remove_dev(dev, class_intf);
}
up(&parent->sem); up(&parent->sem);
class_put(parent); class_put(parent);
} }
int virtual_device_parent(struct device *dev)
{
if (!dev->class)
return -ENODEV;
if (!dev->class->virtual_dir) {
static struct kobject *virtual_dir = NULL;
if (!virtual_dir)
virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
}
dev->kobj.parent = dev->class->virtual_dir;
return 0;
}
int __init classes_init(void) int __init classes_init(void)
{ {
+211 -19
View File
@@ -3,6 +3,8 @@
* *
* Copyright (c) 2002-3 Patrick Mochel * Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs * Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2006 Novell, Inc.
* *
* This file is released under the GPLv2 * This file is released under the GPLv2
* *
@@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj)
if (dev->release) if (dev->release)
dev->release(dev); dev->release(dev);
else if (dev->class && dev->class->dev_release)
dev->class->dev_release(dev);
else { else {
printk(KERN_ERR "Device '%s' does not have a release() function, " printk(KERN_ERR "Device '%s' does not have a release() function, "
"it is broken and must be fixed.\n", "it is broken and must be fixed.\n",
@@ -149,17 +153,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
"MINOR=%u", MINOR(dev->devt)); "MINOR=%u", MINOR(dev->devt));
} }
/* add bus name of physical device */ /* add bus name (same as SUBSYSTEM, deprecated) */
if (dev->bus) if (dev->bus)
add_uevent_var(envp, num_envp, &i, add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length, buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name); "PHYSDEVBUS=%s", dev->bus->name);
/* add driver name of physical device */ /* add driver name (PHYSDEV* values are deprecated)*/
if (dev->driver) if (dev->driver) {
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"DRIVER=%s", dev->driver->name);
add_uevent_var(envp, num_envp, &i, add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length, buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name); "PHYSDEVDRIVER=%s", dev->driver->name);
}
/* terminate, set to next free slot, shrink available space */ /* terminate, set to next free slot, shrink available space */
envp[i] = NULL; envp[i] = NULL;
@@ -177,6 +185,15 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
} }
} }
if (dev->class && dev->class->dev_uevent) {
/* have the class specific function add its stuff */
retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
if (retval) {
pr_debug("%s - dev_uevent() returned %d\n",
__FUNCTION__, retval);
}
}
return retval; return retval;
} }
@@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
return count; return count;
} }
static int device_add_groups(struct device *dev)
{
int i;
int error = 0;
if (dev->groups) {
for (i = 0; dev->groups[i]; i++) {
error = sysfs_create_group(&dev->kobj, dev->groups[i]);
if (error) {
while (--i >= 0)
sysfs_remove_group(&dev->kobj, dev->groups[i]);
goto out;
}
}
}
out:
return error;
}
static void device_remove_groups(struct device *dev)
{
int i;
if (dev->groups) {
for (i = 0; dev->groups[i]; i++) {
sysfs_remove_group(&dev->kobj, dev->groups[i]);
}
}
}
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
int error = 0;
int i;
if (!class)
return 0;
if (class->dev_attrs) {
for (i = 0; attr_name(class->dev_attrs[i]); i++) {
error = device_create_file(dev, &class->dev_attrs[i]);
if (error)
break;
}
}
if (error)
while (--i >= 0)
device_remove_file(dev, &class->dev_attrs[i]);
return error;
}
static void device_remove_attrs(struct device *dev)
{
struct class *class = dev->class;
int i;
if (!class)
return;
if (class->dev_attrs) {
for (i = 0; attr_name(class->dev_attrs[i]); i++)
device_remove_file(dev, &class->dev_attrs[i]);
}
}
static ssize_t show_dev(struct device *dev, struct device_attribute *attr, static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
@@ -236,6 +319,32 @@ void device_remove_file(struct device * dev, struct device_attribute * attr)
} }
} }
/**
* device_create_bin_file - create sysfs binary attribute file for device.
* @dev: device.
* @attr: device binary attribute descriptor.
*/
int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
{
int error = -EINVAL;
if (dev)
error = sysfs_create_bin_file(&dev->kobj, attr);
return error;
}
EXPORT_SYMBOL_GPL(device_create_bin_file);
/**
* device_remove_bin_file - remove sysfs binary attribute file
* @dev: device.
* @attr: device binary attribute descriptor.
*/
void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
{
if (dev)
sysfs_remove_bin_file(&dev->kobj, attr);
}
EXPORT_SYMBOL_GPL(device_remove_bin_file);
static void klist_children_get(struct klist_node *n) static void klist_children_get(struct klist_node *n)
{ {
struct device *dev = container_of(n, struct device, knode_parent); struct device *dev = container_of(n, struct device, knode_parent);
@@ -289,12 +398,20 @@ int device_add(struct device *dev)
{ {
struct device *parent = NULL; struct device *parent = NULL;
char *class_name = NULL; char *class_name = NULL;
struct class_interface *class_intf;
int error = -EINVAL; int error = -EINVAL;
dev = get_device(dev); dev = get_device(dev);
if (!dev || !strlen(dev->bus_id)) if (!dev || !strlen(dev->bus_id))
goto Error; goto Error;
/* if this is a class device, and has no parent, create one */
if ((dev->class) && (dev->parent == NULL)) {
error = virtual_device_parent(dev);
if (error)
goto Error;
}
parent = get_device(dev->parent); parent = get_device(dev->parent);
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
@@ -307,6 +424,10 @@ int device_add(struct device *dev)
if ((error = kobject_add(&dev->kobj))) if ((error = kobject_add(&dev->kobj)))
goto Error; goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.name = "uevent";
dev->uevent_attr.attr.mode = S_IWUSR; dev->uevent_attr.attr.mode = S_IWUSR;
if (dev->driver) if (dev->driver)
@@ -340,12 +461,17 @@ int device_add(struct device *dev)
"subsystem"); "subsystem");
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
dev->bus_id); dev->bus_id);
if (parent) {
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
class_name = make_class_name(dev->class->name, &dev->kobj); class_name = make_class_name(dev->class->name, &dev->kobj);
sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
}
} }
if ((error = device_add_attrs(dev)))
goto AttrsError;
if ((error = device_add_groups(dev)))
goto GroupError;
if ((error = device_pm_add(dev))) if ((error = device_pm_add(dev)))
goto PMError; goto PMError;
if ((error = bus_add_device(dev))) if ((error = bus_add_device(dev)))
@@ -356,15 +482,16 @@ int device_add(struct device *dev)
klist_add_tail(&dev->knode_parent, &parent->klist_children); klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) { if (dev->class) {
/* tie the class to the device */
down(&dev->class->sem); down(&dev->class->sem);
/* tie the class to the device */
list_add_tail(&dev->node, &dev->class->devices); list_add_tail(&dev->node, &dev->class->devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem); up(&dev->class->sem);
} }
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
Done: Done:
kfree(class_name); kfree(class_name);
put_device(dev); put_device(dev);
@@ -372,6 +499,10 @@ int device_add(struct device *dev)
BusError: BusError:
device_pm_remove(dev); device_pm_remove(dev);
PMError: PMError:
device_remove_groups(dev);
GroupError:
device_remove_attrs(dev);
AttrsError:
if (dev->devt_attr) { if (dev->devt_attr) {
device_remove_file(dev, dev->devt_attr); device_remove_file(dev, dev->devt_attr);
kfree(dev->devt_attr); kfree(dev->devt_attr);
@@ -449,6 +580,7 @@ void device_del(struct device * dev)
{ {
struct device * parent = dev->parent; struct device * parent = dev->parent;
char *class_name = NULL; char *class_name = NULL;
struct class_interface *class_intf;
if (parent) if (parent)
klist_del(&dev->knode_parent); klist_del(&dev->knode_parent);
@@ -458,14 +590,23 @@ void device_del(struct device * dev)
sysfs_remove_link(&dev->kobj, "subsystem"); sysfs_remove_link(&dev->kobj, "subsystem");
sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
class_name = make_class_name(dev->class->name, &dev->kobj); class_name = make_class_name(dev->class->name, &dev->kobj);
sysfs_remove_link(&dev->kobj, "device"); if (parent) {
sysfs_remove_link(&dev->parent->kobj, class_name); sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(&dev->parent->kobj, class_name);
}
kfree(class_name); kfree(class_name);
down(&dev->class->sem); down(&dev->class->sem);
/* notify any interfaces that the device is now gone */
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->remove_dev)
class_intf->remove_dev(dev, class_intf);
/* remove the device from the class list */
list_del_init(&dev->node); list_del_init(&dev->node);
up(&dev->class->sem); up(&dev->class->sem);
} }
device_remove_file(dev, &dev->uevent_attr); device_remove_file(dev, &dev->uevent_attr);
device_remove_groups(dev);
device_remove_attrs(dev);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...
@@ -579,7 +720,7 @@ static void device_create_release(struct device *dev)
* been created with a call to class_create(). * been created with a call to class_create().
*/ */
struct device *device_create(struct class *class, struct device *parent, struct device *device_create(struct class *class, struct device *parent,
dev_t devt, char *fmt, ...) dev_t devt, const char *fmt, ...)
{ {
va_list args; va_list args;
struct device *dev = NULL; struct device *dev = NULL;
@@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent,
if (class == NULL || IS_ERR(class)) if (class == NULL || IS_ERR(class))
goto error; goto error;
if (parent == NULL) {
printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
goto error;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) { if (!dev) {
@@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt)
device_unregister(dev); device_unregister(dev);
} }
EXPORT_SYMBOL_GPL(device_destroy); EXPORT_SYMBOL_GPL(device_destroy);
/**
* device_rename - renames a device
* @dev: the pointer to the struct device to be renamed
* @new_name: the new name of the device
*/
int device_rename(struct device *dev, char *new_name)
{
char *old_class_name = NULL;
char *new_class_name = NULL;
char *old_symlink_name = NULL;
int error;
dev = get_device(dev);
if (!dev)
return -EINVAL;
pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
if ((dev->class) && (dev->parent))
old_class_name = make_class_name(dev->class->name, &dev->kobj);
if (dev->class) {
old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
if (!old_symlink_name)
return -ENOMEM;
strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE);
}
strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
error = kobject_rename(&dev->kobj, new_name);
if (old_class_name) {
new_class_name = make_class_name(dev->class->name, &dev->kobj);
if (new_class_name) {
sysfs_create_link(&dev->parent->kobj, &dev->kobj,
new_class_name);
sysfs_remove_link(&dev->parent->kobj, old_class_name);
}
}
if (dev->class) {
sysfs_remove_link(&dev->class->subsys.kset.kobj,
old_symlink_name);
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
dev->bus_id);
}
put_device(dev);
kfree(old_class_name);
kfree(new_class_name);
kfree(old_symlink_name);
return error;
}
+110 -37
View File
@@ -17,6 +17,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kthread.h>
#include "base.h" #include "base.h"
#include "power/power.h" #include "power/power.h"
@@ -38,66 +39,73 @@
* *
* This function must be called with @dev->sem held. * This function must be called with @dev->sem held.
*/ */
void device_bind_driver(struct device * dev) int device_bind_driver(struct device *dev)
{ {
if (klist_node_attached(&dev->knode_driver)) int ret;
return;
if (klist_node_attached(&dev->knode_driver)) {
printk(KERN_WARNING "%s: device %s already bound\n",
__FUNCTION__, kobject_name(&dev->kobj));
return 0;
}
pr_debug("bound device '%s' to driver '%s'\n", pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name); dev->bus_id, dev->driver->name);
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
sysfs_create_link(&dev->driver->kobj, &dev->kobj, ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj)); kobject_name(&dev->kobj));
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver"); if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
"driver");
if (ret)
sysfs_remove_link(&dev->driver->kobj,
kobject_name(&dev->kobj));
}
return ret;
} }
/** struct stupid_thread_structure {
* driver_probe_device - attempt to bind device & driver. struct device_driver *drv;
* @drv: driver. struct device *dev;
* @dev: device. };
*
* First, we call the bus's match function, if one present, which static atomic_t probe_count = ATOMIC_INIT(0);
* should compare the device IDs the driver supports with the static int really_probe(void *void_data)
* device IDs of the device. Note we don't do this ourselves
* because we don't know the format of the ID structures, nor what
* is to be considered a match and what is not.
*
* This function returns 1 if a match is found, an error if one
* occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
*
* This function must be called with @dev->sem held. When called
* for a USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{ {
struct stupid_thread_structure *data = void_data;
struct device_driver *drv = data->drv;
struct device *dev = data->dev;
int ret = 0; int ret = 0;
if (drv->bus->match && !drv->bus->match(dev, drv)) atomic_inc(&probe_count);
goto Done; pr_debug("%s: Probing driver %s with device %s\n",
drv->bus->name, drv->name, dev->bus_id);
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
dev->driver = drv; dev->driver = drv;
if (dev->bus->probe) { if (dev->bus->probe) {
ret = dev->bus->probe(dev); ret = dev->bus->probe(dev);
if (ret) { if (ret) {
dev->driver = NULL; dev->driver = NULL;
goto ProbeFailed; goto probe_failed;
} }
} else if (drv->probe) { } else if (drv->probe) {
ret = drv->probe(dev); ret = drv->probe(dev);
if (ret) { if (ret) {
dev->driver = NULL; dev->driver = NULL;
goto ProbeFailed; goto probe_failed;
} }
} }
device_bind_driver(dev); if (device_bind_driver(dev)) {
printk(KERN_ERR "%s: device_bind_driver(%s) failed\n",
__FUNCTION__, dev->bus_id);
/* How does undo a ->probe? We're screwed. */
}
ret = 1; ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n", pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name); drv->bus->name, dev->bus_id, drv->name);
goto Done; goto done;
ProbeFailed: probe_failed:
if (ret == -ENODEV || ret == -ENXIO) { if (ret == -ENODEV || ret == -ENXIO) {
/* Driver matched, but didn't support device /* Driver matched, but didn't support device
* or device not found. * or device not found.
@@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
"%s: probe of %s failed with error %d\n", "%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret); drv->name, dev->bus_id, ret);
} }
Done: done:
kfree(data);
atomic_dec(&probe_count);
return ret;
}
/**
* driver_probe_done
* Determine if the probe sequence is finished or not.
*
* Should somehow figure out how to use a semaphore, not an atomic variable...
*/
int driver_probe_done(void)
{
pr_debug("%s: probe_count = %d\n", __FUNCTION__,
atomic_read(&probe_count));
if (atomic_read(&probe_count))
return -EBUSY;
return 0;
}
/**
* driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to
* @dev: device to try to bind to the driver
*
* First, we call the bus's match function, if one present, which should
* compare the device IDs the driver supports with the device IDs of the
* device. Note we don't do this ourselves because we don't know the
* format of the ID structures, nor what is to be considered a match and
* what is not.
*
* This function returns 1 if a match is found, an error if one occurs
* (that is not -ENODEV or -ENXIO), and 0 otherwise.
*
* This function must be called with @dev->sem held. When called for a
* USB interface, @dev->parent->sem must be held as well.
*/
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
struct stupid_thread_structure *data;
struct task_struct *probe_task;
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
data = kmalloc(sizeof(*data), GFP_KERNEL);
data->drv = drv;
data->dev = dev;
if (drv->multithread_probe) {
probe_task = kthread_run(really_probe, data,
"probe-%s", dev->bus_id);
if (IS_ERR(probe_task))
ret = PTR_ERR(probe_task);
} else
ret = really_probe(data);
done:
return ret; return ret;
} }
@@ -139,8 +211,9 @@ int device_attach(struct device * dev)
down(&dev->sem); down(&dev->sem);
if (dev->driver) { if (dev->driver) {
device_bind_driver(dev); ret = device_bind_driver(dev);
ret = 1; if (ret == 0)
ret = 1;
} else } else
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
up(&dev->sem); up(&dev->sem);
@@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data)
* returns 0 and the @dev->driver is set, we've found a * returns 0 and the @dev->driver is set, we've found a
* compatible pair. * compatible pair.
*/ */
void driver_attach(struct device_driver * drv) int driver_attach(struct device_driver * drv)
{ {
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
} }
/** /**
+1 -15
View File
@@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
kobject_put(&drv->kobj); kobject_put(&drv->kobj);
} }
static void klist_devices_get(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_driver);
get_device(dev);
}
static void klist_devices_put(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_driver);
put_device(dev);
}
/** /**
* driver_register - register driver with bus * driver_register - register driver with bus
* @drv: driver to register * @drv: driver to register
@@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
(drv->bus->shutdown && drv->shutdown)) { (drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
} }
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&drv->klist_devices, NULL, NULL);
init_completion(&drv->unloaded); init_completion(&drv->unloaded);
return bus_add_driver(drv); return bus_add_driver(drv);
} }
+28 -2
View File
@@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv)
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
} }
static int platform_suspend(struct device * dev, pm_message_t state) static int platform_suspend(struct device *dev, pm_message_t mesg)
{ {
int ret = 0; int ret = 0;
if (dev->driver && dev->driver->suspend) if (dev->driver && dev->driver->suspend)
ret = dev->driver->suspend(dev, state); ret = dev->driver->suspend(dev, mesg);
return ret;
}
static int platform_suspend_late(struct device *dev, pm_message_t mesg)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
int ret = 0;
if (dev->driver && drv->suspend_late)
ret = drv->suspend_late(pdev, mesg);
return ret;
}
static int platform_resume_early(struct device *dev)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
int ret = 0;
if (dev->driver && drv->resume_early)
ret = drv->resume_early(pdev);
return ret; return ret;
} }
@@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
.match = platform_match, .match = platform_match,
.uevent = platform_uevent, .uevent = platform_uevent,
.suspend = platform_suspend, .suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume, .resume = platform_resume,
}; };
EXPORT_SYMBOL_GPL(platform_bus_type); EXPORT_SYMBOL_GPL(platform_bus_type);
+29 -8
View File
@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n"); dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev); error = dev->bus->resume(dev);
} }
if (dev->class && dev->class->resume) {
dev_dbg(dev,"class resume\n");
error = dev->class->resume(dev);
}
up(&dev->sem); up(&dev->sem);
TRACE_RESUME(error); TRACE_RESUME(error);
return error; return error;
} }
static int resume_device_early(struct device * dev)
{
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
if (dev->bus && dev->bus->resume_early) {
dev_dbg(dev,"EARLY resume\n");
error = dev->bus->resume_early(dev);
}
TRACE_RESUME(error);
return error;
}
/*
* Resume the devices that have either not gone through
* the late suspend, or that did go through it but also
* went through the early resume
*/
void dpm_resume(void) void dpm_resume(void)
{ {
down(&dpm_list_sem); down(&dpm_list_sem);
@@ -74,6 +96,7 @@ void dpm_resume(void)
void device_resume(void) void device_resume(void)
{ {
might_sleep();
down(&dpm_sem); down(&dpm_sem);
dpm_resume(); dpm_resume();
up(&dpm_sem); up(&dpm_sem);
@@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume);
/** /**
* device_power_up_irq - Power on some devices. * dpm_power_up - Power on some devices.
* *
* Walk the dpm_off_irq list and power each device up. This * Walk the dpm_off_irq list and power each device up. This
* is used for devices that required they be powered down with * is used for devices that required they be powered down with
* interrupts disabled. As devices are powered on, they are moved to * interrupts disabled. As devices are powered on, they are moved
* the dpm_suspended list. * to the dpm_active list.
* *
* Interrupts must be disabled when calling this. * Interrupts must be disabled when calling this.
*/ */
@@ -99,16 +122,14 @@ void dpm_power_up(void)
struct list_head * entry = dpm_off_irq.next; struct list_head * entry = dpm_off_irq.next;
struct device * dev = to_device(entry); struct device * dev = to_device(entry);
get_device(dev); list_move_tail(entry, &dpm_off);
list_move_tail(entry, &dpm_active); resume_device_early(dev);
resume_device(dev);
put_device(dev);
} }
} }
/** /**
* device_pm_power_up - Turn on all devices that need special attention. * device_power_up - Turn on all devices that need special attention.
* *
* Power on system devices then devices that required we shut them down * Power on system devices then devices that required we shut them down
* with interrupts disabled. * with interrupts disabled.
+62 -30
View File
@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
switch (event) { switch (event) {
case PM_EVENT_SUSPEND: return "suspend"; case PM_EVENT_SUSPEND: return "suspend";
case PM_EVENT_FREEZE: return "freeze"; case PM_EVENT_FREEZE: return "freeze";
case PM_EVENT_PRETHAW: return "prethaw";
default: return "(unknown suspend event)"; default: return "(unknown suspend event)";
} }
} }
@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state)
dev->power.prev_state = dev->power.power_state; dev->power.prev_state = dev->power.power_state;
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { 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);
suspend_report_result(dev->class->suspend, error);
}
if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n", dev_dbg(dev, "%s%s\n",
suspend_verb(state.event), suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND) ((state.event == PM_EVENT_SUSPEND)
@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state)
} }
/*
* This is called with interrupts off, only a single CPU
* running. We can't do down() on a semaphore (and we don't
* need the protection)
*/
static int suspend_device_late(struct device *dev, pm_message_t state)
{
int error = 0;
if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
dev_dbg(dev, "LATE %s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
&& device_may_wakeup(dev))
? ", may wakeup"
: ""
);
error = dev->bus->suspend_late(dev, state);
suspend_report_result(dev->bus->suspend_late, error);
}
return error;
}
/** /**
* device_suspend - Save state and stop all devices in system. * device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in. * @state: Power state to put each device in.
* *
* Walk the dpm_active list, call ->suspend() for each device, and move * Walk the dpm_active list, call ->suspend() for each device, and move
* it to dpm_off. * it to the dpm_off list.
* Check the return value for each. If it returns 0, then we move the *
* the device to the dpm_off list. If it returns -EAGAIN, we move it to * (For historical reasons, if it returns -EAGAIN, that used to mean
* the dpm_off_irq list. If we get a different error, try and back out. * that the device would be called again with interrupts disabled.
* These days, we use the "suspend_late()" callback for that, so we
* print a warning and consider it an error).
*
* If we get a different error, try and back out.
* *
* If we hit a failure with any of the devices, call device_resume() * If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life. * above to bring the suspended devices back to life.
@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
{ {
int error = 0; int error = 0;
might_sleep();
down(&dpm_sem); down(&dpm_sem);
down(&dpm_list_sem); down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) { while (!list_empty(&dpm_active) && error == 0) {
@@ -115,39 +156,27 @@ int device_suspend(pm_message_t state)
/* Check if the device got removed */ /* Check if the device got removed */
if (!list_empty(&dev->power.entry)) { if (!list_empty(&dev->power.entry)) {
/* Move it to the dpm_off or dpm_off_irq list */ /* Move it to the dpm_off list */
if (!error) if (!error)
list_move(&dev->power.entry, &dpm_off); list_move(&dev->power.entry, &dpm_off);
else if (error == -EAGAIN) {
list_move(&dev->power.entry, &dpm_off_irq);
error = 0;
}
} }
if (error) if (error)
printk(KERN_ERR "Could not suspend device %s: " printk(KERN_ERR "Could not suspend device %s: "
"error %d\n", kobject_name(&dev->kobj), error); "error %d%s\n",
kobject_name(&dev->kobj), error,
error == -EAGAIN ? " (please convert to suspend_late)" : "");
put_device(dev); put_device(dev);
} }
up(&dpm_list_sem); up(&dpm_list_sem);
if (error) { if (error)
/* we failed... before resuming, bring back devices from
* dpm_off_irq list back to main dpm_off list, we do want
* to call resume() on them, in case they partially suspended
* despite returning -EAGAIN
*/
while (!list_empty(&dpm_off_irq)) {
struct list_head * entry = dpm_off_irq.next;
list_move(entry, &dpm_off);
}
dpm_resume(); dpm_resume();
}
up(&dpm_sem); up(&dpm_sem);
return error; return error;
} }
EXPORT_SYMBOL_GPL(device_suspend); EXPORT_SYMBOL_GPL(device_suspend);
/** /**
* device_power_down - Shut down special devices. * device_power_down - Shut down special devices.
* @state: Power state to enter. * @state: Power state to enter.
@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
int error = 0; int error = 0;
struct device * dev; struct device * dev;
list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) { while (!list_empty(&dpm_off)) {
if ((error = suspend_device(dev, state))) struct list_head * entry = dpm_off.prev;
break;
dev = to_device(entry);
error = suspend_device_late(dev, state);
if (error)
goto Error;
list_move(&dev->power.entry, &dpm_off_irq);
} }
if (error)
goto Error; error = sysdev_suspend(state);
if ((error = sysdev_suspend(state)))
goto Error;
Done: Done:
return error; return error;
Error: Error:
+25 -10
View File
@@ -7,22 +7,29 @@
#include "power.h" #include "power.h"
#ifdef CONFIG_PM_SYSFS_DEPRECATED
/** /**
* state - Control current power state of device * state - Control current power state of device
* *
* show() returns the current power state of the device. '0' indicates * show() returns the current power state of the device. '0' indicates
* the device is on. Other values (1-3) indicate the device is in a low * the device is on. Other values (2) indicate the device is in some low
* power state. * power state.
* *
* store() sets the current power state, which is an integer value * store() sets the current power state, which is an integer valued
* between 0-3. If the device is on ('0'), and the value written is * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
* greater than 0, then the device is placed directly into the low-power * methods fail this operation; those methods couldn't be called.
* state (via its driver's ->suspend() method). * Otherwise,
* If the device is currently in a low-power state, and the value is 0, *
* the device is powered back on (via the ->resume() method). * - If the recorded dev->power.power_state.event matches the
* If the device is in a low-power state, and a different low-power state * target value, nothing is done.
* is requested, the device is first resumed, then suspended into the new * - If the recorded event code is nonzero, the device is reactivated
* low-power state. * by calling bus.resume() and/or class.resume().
* - If the target value is nonzero, the device is suspended by
* calling class.suspend() and/or bus.suspend() with event code
* PM_EVENT_SUSPEND.
*
* This mechanism is DEPRECATED and should only be used for testing.
*/ */
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
@@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
pm_message_t state; pm_message_t state;
int error = -EINVAL; int error = -EINVAL;
/* disallow incomplete suspend sequences */
if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
return error;
state.event = PM_EVENT_SUSPEND; state.event = PM_EVENT_SUSPEND;
/* Older apps expected to write "3" here - confused with PCI D3 */ /* Older apps expected to write "3" here - confused with PCI D3 */
if ((n == 1) && !strcmp(buf, "3")) if ((n == 1) && !strcmp(buf, "3"))
@@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
static DEVICE_ATTR(state, 0644, state_show, state_store); static DEVICE_ATTR(state, 0644, state_show, state_store);
#endif /* CONFIG_PM_SYSFS_DEPRECATED */
/* /*
* wakeup - Report/change current wakeup option for device * wakeup - Report/change current wakeup option for device
* *
@@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
static struct attribute * power_attrs[] = { static struct attribute * power_attrs[] = {
#ifdef CONFIG_PM_SYSFS_DEPRECATED
&dev_attr_state.attr, &dev_attr_state.attr,
#endif
&dev_attr_wakeup.attr, &dev_attr_wakeup.attr,
NULL, NULL,
}; };
+4 -2
View File
@@ -1207,7 +1207,7 @@ int system_bus_clock (void)
EXPORT_SYMBOL(system_bus_clock); EXPORT_SYMBOL(system_bus_clock);
static int generic_ide_suspend(struct device *dev, pm_message_t state) static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{ {
ide_drive_t *drive = dev->driver_data; ide_drive_t *drive = dev->driver_data;
struct request rq; struct request rq;
@@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
rq.special = &args; rq.special = &args;
rq.end_io_data = &rqpm; rq.end_io_data = &rqpm;
rqpm.pm_step = ide_pm_state_start_suspend; rqpm.pm_step = ide_pm_state_start_suspend;
rqpm.pm_state = state.event; if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
rqpm.pm_state = mesg.event;
return ide_do_drive_cmd(drive, &rq, ide_wait); return ide_do_drive_cmd(drive, &rq, ide_wait);
} }
+8 -6
View File
@@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
} }
static int static int
pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t state) pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
{ {
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev); ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
int rc = 0; int rc = 0;
if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >= PM_EVENT_SUSPEND) { if (mesg.event != mdev->ofdev.dev.power.power_state.event
&& mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif); rc = pmac_ide_do_suspend(hwif);
if (rc == 0) if (rc == 0)
mdev->ofdev.dev.power.power_state = state; mdev->ofdev.dev.power.power_state = mesg;
} }
return rc; return rc;
@@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
} }
static int static int
pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t state) pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{ {
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev); ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
int rc = 0; int rc = 0;
if (state.event != pdev->dev.power.power_state.event && state.event >= 2) { if (mesg.event != pdev->dev.power.power_state.event
&& mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif); rc = pmac_ide_do_suspend(hwif);
if (rc == 0) if (rc == 0)
pdev->dev.power.power_state = state; pdev->dev.power.power_state = mesg;
} }
return rc; return rc;
+1 -1
View File
@@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem)) if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS; return -ERESTARTSYS;
if (state.event > PM_EVENT_ON) { if (1) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
cinergyt2_suspend_rc(cinergyt2); cinergyt2_suspend_rc(cinergyt2);
+25
View File
@@ -17,6 +17,31 @@ config PCI_MSI
If you don't know what to do here, say N. If you don't know what to do here, say N.
config PCI_MULTITHREAD_PROBE
bool "PCI Multi-threaded probe (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL
help
Say Y here if you want the PCI core to spawn a new thread for
every PCI device that is probed. This can cause a huge
speedup in boot times on multiprocessor machines, and even a
smaller speedup on single processor machines.
But it can also cause lots of bad things to happen. A number
of PCI drivers can not properly handle running in this way,
some will just not work properly at all, while others might
decide to blow up power supplies with a huge load all at once,
so use this option at your own risk.
It is very unwise to use this option if you are not using a
boot process that can handle devices being created in any
order. A program that can create persistant block and network
device names (like udev) is a good idea if you wish to use
this option.
Again, use this option at your own risk, you have been warned!
When in doubt, say N.
config PCI_DEBUG config PCI_DEBUG
bool "PCI Debugging" bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL depends on PCI && DEBUG_KERNEL
+1 -3
View File
@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n", __FUNCTION__); err("%s: Notification handler removal failed\n", __FUNCTION__);
/* remove the /sys entries */ /* remove the /sys entries */
if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr)) sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
err("%s: removal of sysfs file apci_table failed\n",
__FUNCTION__);
} }
module_init(ibm_acpiphp_init); module_init(ibm_acpiphp_init);
+37 -1
View File
@@ -17,6 +17,16 @@
* Registration of PCI drivers and handling of hot-pluggable devices. * Registration of PCI drivers and handling of hot-pluggable devices.
*/ */
/* multithreaded probe logic */
static int pci_multithread_probe =
#ifdef CONFIG_PCI_MULTITHREAD_PROBE
1;
#else
0;
#endif
__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
/* /*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG * Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/ */
@@ -279,6 +289,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
return i; return i;
} }
static int pci_device_suspend_late(struct device * dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
int i = 0;
if (drv && drv->suspend_late) {
i = drv->suspend_late(pci_dev, state);
suspend_report_result(drv->suspend_late, i);
}
return i;
}
/* /*
* Default resume method for devices that have no driver provided resume, * Default resume method for devices that have no driver provided resume,
@@ -313,6 +335,17 @@ static int pci_device_resume(struct device * dev)
return error; return error;
} }
static int pci_device_resume_early(struct device * dev)
{
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
if (drv && drv->resume_early)
error = drv->resume_early(pci_dev);
return error;
}
static void pci_device_shutdown(struct device *dev) static void pci_device_shutdown(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -385,6 +418,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
drv->driver.bus = &pci_bus_type; drv->driver.bus = &pci_bus_type;
drv->driver.owner = owner; drv->driver.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type; drv->driver.kobj.ktype = &pci_driver_kobj_type;
drv->driver.multithread_probe = pci_multithread_probe;
spin_lock_init(&drv->dynids.lock); spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list); INIT_LIST_HEAD(&drv->dynids.list);
@@ -509,8 +543,10 @@ struct bus_type pci_bus_type = {
.probe = pci_device_probe, .probe = pci_device_probe,
.remove = pci_device_remove, .remove = pci_device_remove,
.suspend = pci_device_suspend, .suspend = pci_device_suspend,
.shutdown = pci_device_shutdown, .suspend_late = pci_device_suspend_late,
.resume_early = pci_device_resume_early,
.resume = pci_device_resume, .resume = pci_device_resume,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs, .dev_attrs = pci_dev_attrs,
}; };

Some files were not shown because too many files have changed in this diff Show More