1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2006-09-26 11:49:46 -07:00
commit dd77a4ee0f
55 changed files with 1624 additions and 542 deletions

View File

@ -1,13 +1,12 @@
What: devfs
Date: July 2005
Date: July 2005 (scheduled), finally removed in kernel v2.6.18
Contact: Greg Kroah-Hartman <gregkh@suse.de>
Description:
devfs has been unmaintained for a number of years, has unfixable
races, contains a naming policy within the kernel that is
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
kernel tree.
Users:

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.

View File

@ -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)
When: December 2005
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.
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>
---------------------------

View File

@ -1,208 +1,553 @@
Most of the code in Linux is device drivers, so most of the Linux power
management code is also driver-specific. Most drivers will do very little;
others, especially for platforms with small batteries (like cell phones),
will do a lot.
Device Power Management
This writeup gives an overview of how drivers interact with system-wide
power management goals, emphasizing the models and interfaces that are
shared by everything that hooks up to the driver model core. Read it as
background for the domain-specific work you'd do with any specific driver.
Device power management encompasses two areas - the ability to save
state and transition a device to a low-power state when the system is
entering a low-power state; and the ability to transition a device to
a low-power state while the system is running (and independently of
any other power management activity).
Two Models for Device Power Management
======================================
Drivers will use one or both of these models to put devices into low-power
states:
System Sleep model:
Drivers can enter low power states as part of entering system-wide
low-power states like "suspend-to-ram", or (mostly for systems with
disks) "hibernate" (suspend-to-disk).
This is something that device, bus, and class drivers collaborate on
by implementing various role-specific suspend and resume methods to
cleanly power down hardware and software subsystems, then reactivate
them without loss of data.
Some drivers can manage hardware wakeup events, which make the system
leave that low-power state. This feature may be disabled using the
relevant /sys/devices/.../power/wakeup file; enabling it may cost some
power usage, but let the whole system enter low power states more often.
Runtime Power Management model:
Drivers may also enter low power states while the system is running,
independently of other power management activity. Upstream drivers
will normally not know (or care) if the device is in some low power
state when issuing requests; the driver will auto-resume anything
that's needed when it gets a request.
This doesn't have, or need much infrastructure; it's just something you
should do when writing your drivers. For example, clk_disable() unused
clocks as part of minimizing power drain for currently-unused hardware.
Of course, sometimes clusters of drivers will collaborate with each
other, which could involve task-specific power management.
There's not a lot to be said about those low power states except that they
are very system-specific, and often device-specific. Also, that if enough
drivers put themselves into low power states (at "runtime"), the effect may be
the same as entering some system-wide low-power state (system sleep) ... and
that synergies exist, so that several drivers using runtime pm might put the
system into a state where even deeper power saving options are available.
Most suspended devices will have quiesced all I/O: no more DMA or irqs, no
more data read or written, and requests from upstream drivers are no longer
accepted. A given bus or platform may have different requirements though.
Examples of hardware wakeup events include an alarm from a real time clock,
network wake-on-LAN packets, keyboard or mouse activity, and media insertion
or removal (for PCMCIA, MMC/SD, USB, and so on).
Methods
Interfaces for Entering System Sleep States
===========================================
Most of the programming interfaces a device driver needs to know about
relate to that first model: entering a system-wide low power state,
rather than just minimizing power consumption by one device.
The methods to suspend and resume devices reside in struct bus_type:
Bus Driver Methods
------------------
The core methods to suspend and resume devices reside in struct bus_type.
These are mostly of interest to people writing infrastructure for busses
like PCI or USB, or because they define the primitives that device drivers
may need to apply in domain-specific ways to their devices:
struct bus_type {
...
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
...
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
};
Each bus driver is responsible implementing these methods, translating
the call into a bus-specific request and forwarding the call to the
bus-specific drivers. For example, PCI drivers implement suspend() and
resume() methods in struct pci_driver. The PCI core is simply
responsible for translating the pointers to PCI-specific ones and
calling the low-level driver.
Bus drivers implement those methods as appropriate for the hardware and
the drivers using it; PCI works differently from USB, and so on. Not many
people write bus drivers; most driver code is a "device driver" that
builds on top of bus-specific framework code.
This is done to a) ease transition to the new power management methods
and leverage the existing PM code in various bus drivers; b) allow
buses to implement generic and default PM routines for devices, and c)
make the flow of execution obvious to the reader.
For more information on these driver calls, see the description later;
they are called in phases for every device, respecting the parent-child
sequencing in the driver model tree. Note that as this is being written,
only the suspend() and resume() are widely available; not many bus drivers
leverage all of those phases, or pass them down to lower driver levels.
System Power Management
/sys/devices/.../power/wakeup files
-----------------------------------
All devices in the driver model have two flags to control handling of
wakeup events, which are hardware signals that can force the device and/or
system out of a low power state. These are initialized by bus or device
driver code using device_init_wakeup(dev,can_wakeup).
When the system enters a low-power state, the device tree is walked in
a depth-first fashion to transition each device into a low-power
state. The ordering of the device tree is guaranteed by the order in
which devices get registered - children are never registered before
their ancestors, and devices are placed at the back of the list when
registered. By walking the list in reverse order, we are guaranteed to
suspend devices in the proper order.
The "can_wakeup" flag just records whether the device (and its driver) can
physically support wakeup events. When that flag is clear, the sysfs
"wakeup" file is empty, and device_may_wakeup() returns false.
Devices are suspended once with interrupts enabled. Drivers are
expected to stop I/O transactions, save device state, and place the
device into a low-power state. Drivers may sleep, allocate memory,
etc. at will.
For devices that can issue wakeup events, a separate flag controls whether
that device should try to use its wakeup mechanism. The initial value of
device_may_wakeup() will be true, so that the device's "wakeup" file holds
the value "enabled". Userspace can change that to "disabled" so that
device_may_wakeup() returns false; or change it back to "enabled" (so that
it returns true again).
Some devices are broken and will inevitably have problems powering
down or disabling themselves with interrupts enabled. For these
special cases, they may return -EAGAIN. This will put the device on a
list to be taken care of later. When interrupts are disabled, before
we enter the low-power state, their drivers are called again to put
their device to sleep.
On resume, the devices that returned -EAGAIN will be called to power
themselves back on with interrupts disabled. Once interrupts have been
re-enabled, the rest of the drivers will be called to resume their
devices. On resume, a driver is responsible for powering back on each
device, restoring state, and re-enabling I/O transactions for that
device.
EXAMPLE: PCI Device Driver Methods
-----------------------------------
PCI framework software calls these methods when the PCI device driver bound
to a device device has provided them:
struct pci_driver {
...
int (*suspend)(struct pci_device *pdev, pm_message_t state);
int (*suspend_late)(struct pci_device *pdev, pm_message_t state);
int (*resume_early)(struct pci_device *pdev);
int (*resume)(struct pci_device *pdev);
};
Drivers will implement those methods, and call PCI-specific procedures
like pci_set_power_state(), pci_enable_wake(), pci_save_state(), and
pci_restore_state() to manage PCI-specific mechanisms. (PCI config space
could be saved during driver probe, if it weren't for the fact that some
systems rely on userspace tweaking using setpci.) Devices are suspended
before their bridges enter low power states, and likewise bridges resume
before their devices.
Upper Layers of Driver Stacks
-----------------------------
Device drivers generally have at least two interfaces, and the methods
sketched above are the ones which apply to the lower level (nearer PCI, USB,
or other bus hardware). The network and block layers are examples of upper
level interfaces, as is a character device talking to userspace.
Power management requests normally need to flow through those upper levels,
which often use domain-oriented requests like "blank that screen". In
some cases those upper levels will have power management intelligence that
relates to end-user activity, or other devices that work in cooperation.
When those interfaces are structured using class interfaces, there is a
standard way to have the upper layer stop issuing requests to a given
class device (and restart later):
struct class {
...
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
};
Those calls are issued in specific phases of the process by which the
system enters a low power "suspend" state, or resumes from it.
Calling Drivers to Enter System Sleep States
============================================
When the system enters a low power state, each device's driver is asked
to suspend the device by putting it into state compatible with the target
system state. That's usually some version of "off", but the details are
system-specific. Also, wakeup-enabled devices will usually stay partly
functional in order to wake the system.
When the system leaves that low power state, the device's driver is asked
to resume it. The suspend and resume operations always go together, and
both are multi-phase operations.
For simple drivers, suspend might quiesce the device using the class code
and then turn its hardware as "off" as possible with late_suspend. The
matching resume calls would then completely reinitialize the hardware
before reactivating its class I/O queues.
More power-aware drivers drivers will use more than one device low power
state, either at runtime or during system sleep states, and might trigger
system wakeup events.
Call Sequence Guarantees
------------------------
To ensure that bridges and similar links needed to talk to a device are
available when the device is suspended or resumed, the device tree is
walked in a bottom-up order to suspend devices. A top-down order is
used to resume those devices.
The ordering of the device tree is defined by the order in which devices
get registered: a child can never be registered, probed or resumed before
its parent; and can't be removed or suspended after that parent.
The policy is that the device tree should match hardware bus topology.
(Or at least the control bus, for devices which use multiple busses.)
Suspending Devices
------------------
Suspending a given device is done in several phases. Suspending the
system always includes every phase, executing calls for every device
before the next phase begins. Not all busses or classes support all
these callbacks; and not all drivers use all the callbacks.
The phases are seen by driver notifications issued in this order:
1 class.suspend(dev, message) is called after tasks are frozen, for
devices associated with a class that has such a method. This
method may sleep.
Since I/O activity usually comes from such higher layers, this is
a good place to quiesce all drivers of a given type (and keep such
code out of those drivers).
2 bus.suspend(dev, message) is called next. This method may sleep,
and is often morphed into a device driver call with bus-specific
parameters and/or rules.
This call should handle parts of device suspend logic that require
sleeping. It probably does work to quiesce the device which hasn't
been abstracted into class.suspend() or bus.suspend_late().
3 bus.suspend_late(dev, message) is called with IRQs disabled, and
with only one CPU active. Until the bus.resume_early() phase
completes (see later), IRQs are not enabled again. This method
won't be exposed by all busses; for message based busses like USB,
I2C, or SPI, device interactions normally require IRQs. This bus
call may be morphed into a driver call with bus-specific parameters.
This call might save low level hardware state that might otherwise
be lost in the upcoming low power state, and actually put the
device into a low power state ... so that in some cases the device
may stay partly usable until this late. This "late" call may also
help when coping with hardware that behaves badly.
The pm_message_t parameter is currently used to refine those semantics
(described later).
At the end of those phases, drivers should normally have stopped all I/O
transactions (DMA, IRQs), saved enough state that they can re-initialize
or restore previous state (as needed by the hardware), and placed the
device into a low-power state. On many platforms they will also use
clk_disable() to gate off one or more clock sources; sometimes they will
also switch off power supplies, or reduce voltages. Drivers which have
runtime PM support may already have performed some or all of the steps
needed to prepare for the upcoming system sleep state.
When any driver sees that its device_can_wakeup(dev), it should make sure
to use the relevant hardware signals to trigger a system wakeup event.
For example, enable_irq_wake() might identify GPIO signals hooked up to
a switch or other external hardware, and pci_enable_wake() does something
similar for PCI's PME# signal.
If a driver (or bus, or class) fails it suspend method, the system won't
enter the desired low power state; it will resume all the devices it's
suspended so far.
Note that drivers may need to perform different actions based on the target
system lowpower/sleep state. At this writing, there are only platform
specific APIs through which drivers could determine those target states.
Device Low Power (suspend) States
---------------------------------
Device low-power states aren't very standard. One device might only handle
"on" and "off, while another might support a dozen different versions of
"on" (how many engines are active?), plus a state that gets back to "on"
faster than from a full "off".
Some busses define rules about what different suspend states mean. PCI
gives one example: after the suspend sequence completes, a non-legacy
PCI device may not perform DMA or issue IRQs, and any wakeup events it
issues would be issued through the PME# bus signal. Plus, there are
several PCI-standard device states, some of which are optional.
In contrast, integrated system-on-chip processors often use irqs as the
wakeup event sources (so drivers would call enable_irq_wake) and might
be able to treat DMA completion as a wakeup event (sometimes DMA can stay
active too, it'd only be the CPU and some peripherals that sleep).
Some details here may be platform-specific. Systems may have devices that
can be fully active in certain sleep states, such as an LCD display that's
refreshed using DMA while most of the system is sleeping lightly ... and
its frame buffer might even be updated by a DSP or other non-Linux CPU while
the Linux control processor stays idle.
Moreover, the specific actions taken may depend on the target system state.
One target system state might allow a given device to be very operational;
another might require a hard shut down with re-initialization on resume.
And two different target systems might use the same device in different
ways; the aforementioned LCD might be active in one product's "standby",
but a different product using the same SOC might work differently.
Meaning of pm_message_t.event
-----------------------------
Parameters to suspend calls include the device affected and a message of
type pm_message_t, which has one field: the event. If driver does not
recognize the event code, suspend calls may abort the request and return
a negative errno. However, most drivers will be fine if they implement
PM_EVENT_SUSPEND semantics for all messages.
The event codes are used to refine the goal of suspending the device, and
mostly matter when creating or resuming system memory image snapshots, as
used with suspend-to-disk:
PM_EVENT_SUSPEND -- quiesce the driver and put hardware into a low-power
state. When used with system sleep states like "suspend-to-RAM" or
"standby", the upcoming resume() call will often be able to rely on
state kept in hardware, or issue system wakeup events. When used
instead with suspend-to-disk, few devices support this capability;
most are completely powered off.
PM_EVENT_FREEZE -- quiesce the driver, but don't necessarily change into
any low power mode. A system snapshot is about to be taken, often
followed by a call to the driver's resume() method. Neither wakeup
events nor DMA are allowed.
PM_EVENT_PRETHAW -- quiesce the driver, knowing that the upcoming resume()
will restore a suspend-to-disk snapshot from a different kernel image.
Drivers that are smart enough to look at their hardware state during
resume() processing need that state to be correct ... a PRETHAW could
be used to invalidate that state (by resetting the device), like a
shutdown() invocation would before a kexec() or system halt. Other
drivers might handle this the same way as PM_EVENT_FREEZE. Neither
wakeup events nor DMA are allowed.
To enter "standby" (ACPI S1) or "Suspend to RAM" (STR, ACPI S3) states, or
the similarly named APM states, only PM_EVENT_SUSPEND is used; for "Suspend
to Disk" (STD, hibernate, ACPI S4), all of those event codes are used.
There's also PM_EVENT_ON, a value which never appears as a suspend event
but is sometimes used to record the "not suspended" device state.
Resuming Devices
----------------
Resuming is done in multiple phases, much like suspending, with all
devices processing each phase's calls before the next phase begins.
The phases are seen by driver notifications issued in this order:
1 bus.resume_early(dev) is called with IRQs disabled, and with
only one CPU active. As with bus.suspend_late(), this method
won't be supported on busses that require IRQs in order to
interact with devices.
This reverses the effects of bus.suspend_late().
2 bus.resume(dev) is called next. This may be morphed into a device
driver call with bus-specific parameters; implementations may sleep.
This reverses the effects of bus.suspend().
3 class.resume(dev) is called for devices associated with a class
that has such a method. Implementations may sleep.
This reverses the effects of class.suspend(), and would usually
reactivate the device's I/O queue.
At the end of those phases, drivers should normally be as functional as
they were before suspending: I/O can be performed using DMA and IRQs, and
the relevant clocks are gated on. The device need not be "fully on"; it
might be in a runtime lowpower/suspend state that acts as if it were.
However, the details here may again be platform-specific. For example,
some systems support multiple "run" states, and the mode in effect at
the end of resume() might not be the one which preceded suspension.
That means availability of certain clocks or power supplies changed,
which could easily affect how a driver works.
Drivers need to be able to handle hardware which has been reset since the
suspend methods were called, for example by complete reinitialization.
This may be the hardest part, and the one most protected by NDA'd documents
and chip errata. It's simplest if the hardware state hasn't changed since
the suspend() was called, but that can't always be guaranteed.
Drivers must also be prepared to notice that the device has been removed
while the system was powered off, whenever that's physically possible.
PCMCIA, MMC, USB, Firewire, SCSI, and even IDE are common examples of busses
where common Linux platforms will see such removal. Details of how drivers
will notice and handle such removals are currently bus-specific, and often
involve a separate thread.
Note that the bus-specific runtime PM wakeup mechanism can exist, and might
be defined to share some of the same driver code as for system wakeup. For
example, a bus-specific device driver's resume() method might be used there,
so it wouldn't only be called from bus.resume() during system-wide wakeup.
See bus-specific information about how runtime wakeup events are handled.
System Devices
--------------
System devices follow a slightly different API, which can be found in
include/linux/sysdev.h
drivers/base/sys.c
System devices will only be suspended with interrupts disabled, and
after all other devices have been suspended. On resume, they will be
resumed before any other devices, and also with interrupts disabled.
System devices will only be suspended with interrupts disabled, and after
all other devices have been suspended. On resume, they will be resumed
before any other devices, and also with interrupts disabled.
That is, IRQs are disabled, the suspend_late() phase begins, then the
sysdev_driver.suspend() phase, and the system enters a sleep state. Then
the sysdev_driver.resume() phase begins, followed by the resume_early()
phase, after which IRQs are enabled.
Code to actually enter and exit the system-wide low power state sometimes
involves hardware details that are only known to the boot firmware, and
may leave a CPU running software (from SRAM or flash memory) that monitors
the system and manages its wakeup sequence.
Runtime Power Management
========================
Many devices are able to dynamically power down while the system is still
running. This feature is useful for devices that are not being used, and
can offer significant power savings on a running system. These devices
often support a range of runtime power states, which might use names such
as "off", "sleep", "idle", "active", and so on. Those states will in some
cases (like PCI) be partially constrained by a bus the device uses, and will
usually include hardware states that are also used in system sleep states.
Many devices are able to dynamically power down while the system is
still running. This feature is useful for devices that are not being
used, and can offer significant power savings on a running system.
However, note that if a driver puts a device into a runtime low power state
and the system then goes into a system-wide sleep state, it normally ought
to resume into that runtime low power state rather than "full on". Such
distinctions would be part of the driver-internal state machine for that
hardware; the whole point of runtime power management is to be sure that
drivers are decoupled in that way from the state machine governing phases
of the system-wide power/sleep state transitions.
In each device's directory, there is a 'power' directory, which
contains at least a 'state' file. Reading from this file displays what
power state the device is currently in. Writing to this file initiates
a transition to the specified power state, which must be a decimal in
the range 1-3, inclusive; or 0 for 'On'.
The PM core will call the ->suspend() method in the bus_type object
that the device belongs to if the specified state is not 0, or
->resume() if it is.
Power Saving Techniques
-----------------------
Normally runtime power management is handled by the drivers without specific
userspace or kernel intervention, by device-aware use of techniques like:
Nothing will happen if the specified state is the same state the
device is currently in.
Using information provided by other system layers
- stay deeply "off" except between open() and close()
- if transceiver/PHY indicates "nobody connected", stay "off"
- application protocols may include power commands or hints
If the device is already in a low-power state, and the specified state
is another, but different, low-power state, the ->resume() method will
first be called to power the device back on, then ->suspend() will be
called again with the new state.
Using fewer CPU cycles
- using DMA instead of PIO
- removing timers, or making them lower frequency
- shortening "hot" code paths
- eliminating cache misses
- (sometimes) offloading work to device firmware
The driver is responsible for saving the working state of the device
and putting it into the low-power state specified. If this was
successful, it returns 0, and the device's power_state field is
updated.
Reducing other resource costs
- gating off unused clocks in software (or hardware)
- switching off unused power supplies
- eliminating (or delaying/merging) IRQs
- tuning DMA to use word and/or burst modes
The driver must take care to know whether or not it is able to
properly resume the device, including all step of reinitialization
necessary. (This is the hardest part, and the one most protected by
NDA'd documents).
Using device-specific low power states
- using lower voltages
- avoiding needless DMA transfers
The driver must also take care not to suspend a device that is
currently in use. It is their responsibility to provide their own
exclusion mechanisms.
Read your hardware documentation carefully to see the opportunities that
may be available. If you can, measure the actual power usage and check
it against the budget established for your project.
The runtime power transition happens with interrupts enabled. If a
device cannot support being powered down with interrupts, it may
return -EAGAIN (as it would during a system power management
transition), but it will _not_ be called again, and the transaction
will fail.
There is currently no way to know what states a device or driver
supports a priori. This will change in the future.
Examples: USB hosts, system timer, system CPU
----------------------------------------------
USB host controllers make interesting, if complex, examples. In many cases
these have no work to do: no USB devices are connected, or all of them are
in the USB "suspend" state. Linux host controller drivers can then disable
periodic DMA transfers that would otherwise be a constant power drain on the
memory subsystem, and enter a suspend state. In power-aware controllers,
entering that suspend state may disable the clock used with USB signaling,
saving a certain amount of power.
pm_message_t meaning
The controller will be woken from that state (with an IRQ) by changes to the
signal state on the data lines of a given port, for example by an existing
peripheral requesting "remote wakeup" or by plugging a new peripheral. The
same wakeup mechanism usually works from "standby" sleep states, and on some
systems also from "suspend to RAM" (or even "suspend to disk") states.
(Except that ACPI may be involved instead of normal IRQs, on some hardware.)
pm_message_t has two fields. event ("major"), and flags. If driver
does not know event code, it aborts the request, returning error. Some
drivers may need to deal with special cases based on the actual type
of suspend operation being done at the system level. This is why
there are flags.
System devices like timers and CPUs may have special roles in the platform
power management scheme. For example, system timers using a "dynamic tick"
approach don't just save CPU cycles (by eliminating needless timer IRQs),
but they may also open the door to using lower power CPU "idle" states that
cost more than a jiffie to enter and exit. On x86 systems these are states
like "C3"; note that periodic DMA transfers from a USB host controller will
also prevent entry to a C3 state, much like a periodic timer IRQ.
Event codes are:
That kind of runtime mechanism interaction is common. "System On Chip" (SOC)
processors often have low power idle modes that can't be entered unless
certain medium-speed clocks (often 12 or 48 MHz) are gated off. When the
drivers gate those clocks effectively, then the system idle task may be able
to use the lower power idle modes and thereby increase battery life.
ON -- no need to do anything except special cases like broken
HW.
If the CPU can have a "cpufreq" driver, there also may be opportunities
to shift to lower voltage settings and reduce the power cost of executing
a given number of instructions. (Without voltage adjustment, it's rare
for cpufreq to save much power; the cost-per-instruction must go down.)
# NOTIFICATION -- pretty much same as ON?
FREEZE -- stop DMA and interrupts, and be prepared to reinit HW from
scratch. That probably means stop accepting upstream requests, the
actual policy of what to do with them being specific to a given
driver. It's acceptable for a network driver to just drop packets
while a block driver is expected to block the queue so no request is
lost. (Use IDE as an example on how to do that). FREEZE requires no
power state change, and it's expected for drivers to be able to
quickly transition back to operating state.
/sys/devices/.../power/state files
==================================
For now you can also test some of this functionality using sysfs.
SUSPEND -- like FREEZE, but also put hardware into low-power state. If
there's need to distinguish several levels of sleep, additional flag
is probably best way to do that.
DEPRECATED: USE "power/state" ONLY FOR DRIVER TESTING, AND
AVOID USING dev->power.power_state IN DRIVERS.
Transitions are only from a resumed state to a suspended state, never
between 2 suspended states. (ON -> FREEZE or ON -> SUSPEND can happen,
FREEZE -> SUSPEND or SUSPEND -> FREEZE can not).
THESE WILL BE REMOVED. IF THE "power/state" FILE GETS REPLACED,
IT WILL BECOME SOMETHING COUPLED TO THE BUS OR DRIVER.
All events are:
In each device's directory, there is a 'power' directory, which contains
at least a 'state' file. The value of this field is effectively boolean,
PM_EVENT_ON or PM_EVENT_SUSPEND.
[NOTE NOTE NOTE: If you are driver author, you should not care; you
should only look at event, and ignore flags.]
* Reading from this file displays a value corresponding to
the power.power_state.event field. All nonzero values are
displayed as "2", corresponding to a low power state; zero
is displayed as "0", corresponding to normal operation.
#Prepare for suspend -- userland is still running but we are going to
#enter suspend state. This gives drivers chance to load firmware from
#disk and store it in memory, or do other activities taht require
#operating userland, ability to kmalloc GFP_KERNEL, etc... All of these
#are forbiden once the suspend dance is started.. event = ON, flags =
#PREPARE_TO_SUSPEND
* Writing to this file initiates a transition using the
specified event code number; only '0', '2', and '3' are
accepted (without a newline); '2' and '3' are both
mapped to PM_EVENT_SUSPEND.
Apm standby -- prepare for APM event. Quiesce devices to make life
easier for APM BIOS. event = FREEZE, flags = APM_STANDBY
On writes, the PM core relies on that recorded event code and the device/bus
capabilities to determine whether it uses a partial suspend() or resume()
sequence to change things so that the recorded event corresponds to the
numeric parameter.
Apm suspend -- same as APM_STANDBY, but it we should probably avoid
spinning down disks. event = FREEZE, flags = APM_SUSPEND
- If the bus requires the irqs-disabled suspend_late()/resume_early()
phases, writes fail because those operations are not supported here.
System halt, reboot -- quiesce devices to make life easier for BIOS. event
= FREEZE, flags = SYSTEM_HALT or SYSTEM_REBOOT
- If the recorded value is the expected value, nothing is done.
System shutdown -- at least disks need to be spun down, or data may be
lost. Quiesce devices, just to make life easier for BIOS. event =
FREEZE, flags = SYSTEM_SHUTDOWN
- If the recorded value is nonzero, the device is partially resumed,
using the bus.resume() and/or class.resume() methods.
Kexec -- turn off DMAs and put hardware into some state where new
kernel can take over. event = FREEZE, flags = KEXEC
- If the target value is nonzero, the device is partially suspended,
using the class.suspend() and/or bus.suspend() methods and the
PM_EVENT_SUSPEND message.
Powerdown at end of swsusp -- very similar to SYSTEM_SHUTDOWN, except wake
may need to be enabled on some devices. This actually has at least 3
subtypes, system can reboot, enter S4 and enter S5 at the end of
swsusp. event = FREEZE, flags = SWSUSP and one of SYSTEM_REBOOT,
SYSTEM_SHUTDOWN, SYSTEM_S4
Suspend to ram -- put devices into low power state. event = SUSPEND,
flags = SUSPEND_TO_RAM
Freeze for swsusp snapshot -- stop DMA and interrupts. No need to put
devices into low power mode, but you must be able to reinitialize
device from scratch in resume method. This has two flavors, its done
once on suspending kernel, once on resuming kernel. event = FREEZE,
flags = DURING_SUSPEND or DURING_RESUME
Device detach requested from /sys -- deinitialize device; proably same as
SYSTEM_SHUTDOWN, I do not understand this one too much. probably event
= FREEZE, flags = DEV_DETACH.
#These are not really events sent:
#
#System fully on -- device is working normally; this is probably never
#passed to suspend() method... event = ON, flags = 0
#
#Ready after resume -- userland is now running, again. Time to free any
#memory you ate during prepare to suspend... event = ON, flags =
#READY_AFTER_RESUME
#
Drivers have no way to tell whether their suspend() and resume() calls
have come through the sysfs power/state file or as part of entering a
system sleep state, except that when accessed through sysfs the normal
parent/child sequencing rules are ignored. Drivers (such as bus, bridge,
or hub drivers) which expose child devices may need to enforce those rules
on their own.

View File

@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
extern int attribute_container_init(void);
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 struct bus_type *get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus);

View File

@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev);
if (!error) {
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
}
if (error)
goto out;
error = sysfs_create_link(&bus->devices.kobj,
&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;
}
@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
* bus_attach_device - add device to bus
* @dev: device tried to attach to a driver
*
* - Add device to bus's list of devices.
* - 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) {
device_attach(dev);
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
dev->is_registered = 1;
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->bus->devices.kobj, dev->bus_id);
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);
device_release_driver(dev);
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
* 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);
driver_create_file(drv, &driver_attr_bind);
int ret;
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)
@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_unbind);
}
#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) {}
#endif
@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
* @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);
int error = 0;
@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
if (bus) {
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
if (error) {
put_bus(bus);
return error;
}
if (error)
goto out_put_bus;
drv->kobj.kset = &bus->drivers;
if ((error = kobject_register(&drv->kobj))) {
put_bus(bus);
return error;
}
if ((error = kobject_register(&drv->kobj)))
goto out_put_bus;
driver_attach(drv);
error = driver_attach(drv);
if (error)
goto out_unregister;
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv);
add_bind_files(drv);
error = driver_add_attrs(bus, 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;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
put_bus(bus);
return error;
}
/**
* bus_remove_driver - delete driver from bus's knowledge.
* @drv: driver.
@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
/* 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->parent) /* Needed for USB */
down(&dev->parent->sem);
device_attach(dev);
ret = device_attach(dev);
if (dev->parent)
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
* 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
* driver attachment should change accordingly.
*/
void device_reprobe(struct device *dev)
int device_reprobe(struct device *dev)
{
if (dev->driver) {
if (dev->parent) /* Needed for USB */
@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
if (dev->parent)
up(&dev->parent->sem);
}
bus_rescan_devices_helper(dev, NULL);
return bus_rescan_devices_helper(dev, NULL);
}
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)
@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
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: bus.
@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
goto bus_drivers_fail;
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);
pr_debug("bus type '%s' registered\n", bus->name);

View File

@ -19,6 +19,8 @@
#include <linux/slab.h>
#include "base.h"
extern struct subsystem devices_subsys;
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#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
* 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;
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);
if (class_dev->dev) {
/* add physical device, backing this device */
/* add device, backing this class device (deprecated) */
struct device *dev = class_dev->dev;
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 *parent,
dev_t devt,
struct device *device, char *fmt, ...)
struct device *device,
const char *fmt, ...)
{
va_list args;
struct class_device *class_dev = NULL;
@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
{
struct class *parent;
struct class_device *class_dev;
struct device *dev;
if (!class_intf || !class_intf->class)
return -ENODEV;
@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
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);
return 0;
@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
{
struct class * parent = class_intf->class;
struct class_device *class_dev;
struct device *dev;
if (!parent)
return;
@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
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);
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)
{

View File

@ -3,6 +3,8 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* 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
*
@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj)
if (dev->release)
dev->release(dev);
else if (dev->class && dev->class->dev_release)
dev->class->dev_release(dev);
else {
printk(KERN_ERR "Device '%s' does not have a release() function, "
"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));
}
/* add bus name of physical device */
/* add bus name (same as SUBSYSTEM, deprecated) */
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name);
/* add driver name of physical device */
if (dev->driver)
/* add driver name (PHYSDEV* values are deprecated)*/
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,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name);
}
/* terminate, set to next free slot, shrink available space */
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;
}
@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
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,
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)
{
struct device *dev = container_of(n, struct device, knode_parent);
@ -289,12 +398,20 @@ int device_add(struct device *dev)
{
struct device *parent = NULL;
char *class_name = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id))
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);
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)))
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
dev->uevent_attr.attr.name = "uevent";
dev->uevent_attr.attr.mode = S_IWUSR;
if (dev->driver)
@ -340,12 +461,17 @@ int device_add(struct device *dev)
"subsystem");
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
dev->bus_id);
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
class_name = make_class_name(dev->class->name, &dev->kobj);
sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
if (parent) {
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
class_name = make_class_name(dev->class->name, &dev->kobj);
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)))
goto PMError;
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);
if (dev->class) {
/* tie the class to the device */
down(&dev->class->sem);
/* tie the class to the device */
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);
}
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
Done:
kfree(class_name);
put_device(dev);
@ -372,6 +499,10 @@ int device_add(struct device *dev)
BusError:
device_pm_remove(dev);
PMError:
device_remove_groups(dev);
GroupError:
device_remove_attrs(dev);
AttrsError:
if (dev->devt_attr) {
device_remove_file(dev, dev->devt_attr);
kfree(dev->devt_attr);
@ -449,6 +580,7 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
char *class_name = NULL;
struct class_interface *class_intf;
if (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->class->subsys.kset.kobj, dev->bus_id);
class_name = make_class_name(dev->class->name, &dev->kobj);
sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(&dev->parent->kobj, class_name);
if (parent) {
sysfs_remove_link(&dev->kobj, "device");
sysfs_remove_link(&dev->parent->kobj, class_name);
}
kfree(class_name);
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);
up(&dev->class->sem);
}
device_remove_file(dev, &dev->uevent_attr);
device_remove_groups(dev);
device_remove_attrs(dev);
/* Notify the platform of the removal, in case they
* need to do anything...
@ -579,7 +720,7 @@ static void device_create_release(struct device *dev)
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, char *fmt, ...)
dev_t devt, const char *fmt, ...)
{
va_list args;
struct device *dev = NULL;
@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent,
if (class == NULL || IS_ERR(class))
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);
if (!dev) {
@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt)
device_unregister(dev);
}
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;
}

View File

@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include "base.h"
#include "power/power.h"
@ -38,66 +39,73 @@
*
* 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))
return;
int ret;
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",
dev->bus_id, dev->driver->name);
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));
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;
}
/**
* driver_probe_device - attempt to bind device & driver.
* @drv: driver.
* @dev: device.
*
* 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 {
struct device_driver *drv;
struct device *dev;
};
static atomic_t probe_count = ATOMIC_INIT(0);
static int really_probe(void *void_data)
{
struct stupid_thread_structure *data = void_data;
struct device_driver *drv = data->drv;
struct device *dev = data->dev;
int ret = 0;
if (drv->bus->match && !drv->bus->match(dev, drv))
goto Done;
atomic_inc(&probe_count);
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;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) {
dev->driver = NULL;
goto ProbeFailed;
goto probe_failed;
}
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
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;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
goto Done;
goto done;
ProbeFailed:
probe_failed:
if (ret == -ENODEV || ret == -ENXIO) {
/* Driver matched, but didn't support device
* 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",
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;
}
@ -139,8 +211,9 @@ int device_attach(struct device * dev)
down(&dev->sem);
if (dev->driver) {
device_bind_driver(dev);
ret = 1;
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
} else
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
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
* 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);
}
/**

View File

@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
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
* @drv: driver to register
@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
(drv->bus->shutdown && drv->shutdown)) {
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);
return bus_add_driver(drv);
}

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);
}
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;
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;
}
@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

View File

@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n");
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);
TRACE_RESUME(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)
{
down(&dpm_list_sem);
@ -74,6 +96,7 @@ void dpm_resume(void)
void device_resume(void)
{
might_sleep();
down(&dpm_sem);
dpm_resume();
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
* is used for devices that required they be powered down with
* interrupts disabled. As devices are powered on, they are moved to
* the dpm_suspended list.
* interrupts disabled. As devices are powered on, they are moved
* to the dpm_active list.
*
* 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 device * dev = to_device(entry);
get_device(dev);
list_move_tail(entry, &dpm_active);
resume_device(dev);
put_device(dev);
list_move_tail(entry, &dpm_off);
resume_device_early(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
* with interrupts disabled.

View File

@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
switch (event) {
case PM_EVENT_SUSPEND: return "suspend";
case PM_EVENT_FREEZE: return "freeze";
case PM_EVENT_PRETHAW: return "prethaw";
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;
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",
suspend_verb(state.event),
((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.
* @state: Power state to put each device in.
*
* Walk the dpm_active list, call ->suspend() for each device, and move
* it to dpm_off.
* 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
* the dpm_off_irq list. If we get a different error, try and back out.
* it to the dpm_off list.
*
* (For historical reasons, if it returns -EAGAIN, that used to mean
* 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()
* above to bring the suspended devices back to life.
@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
{
int error = 0;
might_sleep();
down(&dpm_sem);
down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) {
@ -115,39 +156,27 @@ int device_suspend(pm_message_t state)
/* Check if the device got removed */
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)
list_move(&dev->power.entry, &dpm_off);
else if (error == -EAGAIN) {
list_move(&dev->power.entry, &dpm_off_irq);
error = 0;
}
}
if (error)
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);
}
up(&dpm_list_sem);
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);
}
if (error)
dpm_resume();
}
up(&dpm_sem);
return error;
}
EXPORT_SYMBOL_GPL(device_suspend);
/**
* device_power_down - Shut down special devices.
* @state: Power state to enter.
@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
int error = 0;
struct device * dev;
list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
if ((error = suspend_device(dev, state)))
break;
while (!list_empty(&dpm_off)) {
struct list_head * entry = dpm_off.prev;
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;
if ((error = sysdev_suspend(state)))
goto Error;
error = sysdev_suspend(state);
Done:
return error;
Error:

View File

@ -7,22 +7,29 @@
#include "power.h"
#ifdef CONFIG_PM_SYSFS_DEPRECATED
/**
* state - Control current power state of device
*
* 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.
*
* store() sets the current power state, which is an integer value
* between 0-3. If the device is on ('0'), and the value written is
* greater than 0, then the device is placed directly into the low-power
* state (via its driver's ->suspend() method).
* 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 device is in a low-power state, and a different low-power state
* is requested, the device is first resumed, then suspended into the new
* low-power state.
* store() sets the current power state, which is an integer valued
* 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
* methods fail this operation; those methods couldn't be called.
* Otherwise,
*
* - If the recorded dev->power.power_state.event matches the
* target value, nothing is done.
* - If the recorded event code is nonzero, the device is reactivated
* 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)
@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
pm_message_t state;
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;
/* Older apps expected to write "3" here - confused with PCI D3 */
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);
#endif /* CONFIG_PM_SYSFS_DEPRECATED */
/*
* 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[] = {
#ifdef CONFIG_PM_SYSFS_DEPRECATED
&dev_attr_state.attr,
#endif
&dev_attr_wakeup.attr,
NULL,
};

View File

@ -1207,7 +1207,7 @@ int system_bus_clock (void)
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;
struct request rq;
@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
rq.special = &args;
rq.end_io_data = &rqpm;
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);
}

View File

@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
}
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);
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);
if (rc == 0)
mdev->ofdev.dev.power.power_state = state;
mdev->ofdev.dev.power.power_state = mesg;
}
return rc;
@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
}
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);
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);
if (rc == 0)
pdev->dev.power.power_state = state;
pdev->dev.power.power_state = mesg;
}
return rc;

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))
return -ERESTARTSYS;
if (state.event > PM_EVENT_ON) {
if (1) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
cinergyt2_suspend_rc(cinergyt2);

View File

@ -17,6 +17,31 @@ config PCI_MSI
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
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL

View File

@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n", __FUNCTION__);
/* remove the /sys entries */
if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
err("%s: removal of sysfs file apci_table failed\n",
__FUNCTION__);
sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
}
module_init(ibm_acpiphp_init);

View File

@ -17,6 +17,16 @@
* 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
*/
@ -279,6 +289,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
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,
@ -313,6 +335,17 @@ static int pci_device_resume(struct device * dev)
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)
{
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.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
drv->driver.multithread_probe = pci_multithread_probe;
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@ -509,8 +543,10 @@ struct bus_type pci_bus_type = {
.probe = pci_device_probe,
.remove = pci_device_remove,
.suspend = pci_device_suspend,
.shutdown = pci_device_shutdown,
.suspend_late = pci_device_suspend_late,
.resume_early = pci_device_resume_early,
.resume = pci_device_resume,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
};

View File

@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
case PM_EVENT_ON:
return PCI_D0;
case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
/* REVISIT both freeze and pre-thaw "should" use D0 */
case PM_EVENT_SUSPEND:
return PCI_D3hot;
default:
printk("They asked me for state %d\n", state.event);
printk("Unrecognized suspend event %d\n", state.event);
BUG();
}
return PCI_D0;

View File

@ -1756,16 +1756,23 @@ static void set_mesh_power(struct mesh_state *ms, int state)
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
msleep(10);
}
}
}
#ifdef CONFIG_PM
static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags;
if (state.event == mdev->ofdev.dev.power.power_state.event || state.event < 2)
switch (mesg.event) {
case PM_EVENT_SUSPEND:
case PM_EVENT_FREEZE:
break;
default:
return 0;
}
if (mesg.event == mdev->ofdev.dev.power.power_state.event)
return 0;
scsi_block_requests(ms->host);
@ -1780,7 +1787,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
mdev->ofdev.dev.power.power_state = state;
mdev->ofdev.dev.power.power_state = mesg;
return 0;
}

View File

@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
(void) usb_hcd_pci_resume (dev);
}
} else {
} else if (hcd->state != HC_STATE_HALT) {
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
WARN_ON(1);

View File

@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
writel (0, &ehci->regs->intr_enable);
(void)readl(&ehci->regs->intr_enable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW) {
ehci_halt(ehci);
ehci_reset(ehci);
}
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ehci->lock, flags);

View File

@ -135,6 +135,11 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
}
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
ohci_usb_reset(ohci);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ohci->lock, flags);

View File

@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
struct sl811 *sl811 = hcd_to_sl811(hcd);
int retval = 0;
if (state.event == PM_EVENT_FREEZE)
switch (state.event) {
case PM_EVENT_FREEZE:
retval = sl811h_bus_suspend(hcd);
else if (state.event == PM_EVENT_SUSPEND)
break;
case PM_EVENT_SUSPEND:
case PM_EVENT_PRETHAW: /* explicitly discard hw state */
port_power(sl811, 0);
break;
}
if (retval == 0)
dev->dev.power.power_state = state;
return retval;

View File

@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
/* FIXME: Enable non-PME# remote wakeup? */
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
uhci_hc_died(uhci);
done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
done:

View File

@ -2621,25 +2621,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
}
int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct radeonfb_info *rinfo = info->par;
int i;
if (state.event == pdev->dev.power.power_state.event)
if (mesg.event == pdev->dev.power.power_state.event)
return 0;
printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
pci_name(pdev), state.event);
printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
pci_name(pdev), mesg.event);
/* For suspend-to-disk, we cheat here. We don't suspend anything and
* let fbcon continue drawing until we are all set. That shouldn't
* really cause any problem at this point, provided that the wakeup
* code knows that any state in memory may not match the HW
*/
if (state.event == PM_EVENT_FREEZE)
switch (mesg.event) {
case PM_EVENT_FREEZE: /* about to take snapshot */
case PM_EVENT_PRETHAW: /* before restoring snapshot */
goto done;
}
acquire_console_sem();
@ -2706,7 +2709,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
release_console_sem();
done:
pdev->dev.power.power_state = state;
pdev->dev.power.power_state = mesg;
return 0;
}

View File

@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = {
/***********************************************************************
* Power Management *
***********************************************************************/
static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
static int i810fb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct i810fb_par *par = info->par;
par->cur_state = state.event;
par->cur_state = mesg.event;
if (state.event == PM_EVENT_FREEZE) {
dev->dev.power.power_state = state;
switch (mesg.event) {
case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
dev->dev.power.power_state = mesg;
return 0;
}
@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
pci_save_state(dev);
pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;

View File

@ -950,24 +950,25 @@ static struct fb_ops nvidia_fb_ops = {
};
#ifdef CONFIG_PM
static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct nvidia_par *par = info->par;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
acquire_console_sem();
par->pm_state = state.event;
par->pm_state = mesg.event;
if (state.event == PM_EVENT_FREEZE) {
dev->dev.power.power_state = state;
} else {
if (mesg.event == PM_EVENT_SUSPEND) {
fb_set_suspend(info, 1);
nvidiafb_blank(FB_BLANK_POWERDOWN, info);
nvidia_write_regs(par, &par->SavedReg);
pci_save_state(dev);
pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
pci_set_power_state(dev, pci_choose_state(dev, mesg));
}
dev->dev.power.power_state = mesg;
release_console_sem();
return 0;

View File

@ -2323,24 +2323,24 @@ static void __devexit savagefb_remove(struct pci_dev *dev)
}
}
static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
static int savagefb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par;
DBG("savagefb_suspend");
par->pm_state = state.event;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
par->pm_state = mesg.event;
dev->dev.power.power_state = mesg;
/*
* For PM_EVENT_FREEZE, do not power down so the console
* can remain active.
*/
if (state.event == PM_EVENT_FREEZE) {
dev->dev.power.power_state = state;
if (mesg.event == PM_EVENT_FREEZE)
return 0;
}
acquire_console_sem();
fb_set_suspend(info, 1);
@ -2353,7 +2353,7 @@ static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
savage_disable_mmio(par);
pci_save_state(dev);
pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;

View File

@ -55,12 +55,11 @@ static u64 debugfs_u8_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
/**
* debugfs_create_u8 - create a file in the debugfs filesystem that is used to read and write an unsigned 8 bit value.
*
* debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write
* from.
@ -72,11 +71,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_u8(const char *name, mode_t mode,
@ -97,12 +96,11 @@ static u64 debugfs_u16_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
/**
* debugfs_create_u16 - create a file in the debugfs filesystem that is used to read and write an unsigned 16 bit value.
*
* debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write
* from.
@ -114,11 +112,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_u16(const char *name, mode_t mode,
@ -139,12 +137,11 @@ static u64 debugfs_u32_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
/**
* debugfs_create_u32 - create a file in the debugfs filesystem that is used to read and write an unsigned 32 bit value.
*
* debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write
* from.
@ -156,11 +153,11 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_u32(const char *name, mode_t mode,
@ -219,12 +216,11 @@ static const struct file_operations fops_bool = {
};
/**
* debugfs_create_bool - create a file in the debugfs filesystem that is used to read and write a boolean value.
*
* debugfs_create_bool - create a debugfs file that is used to read and write a boolean value
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @value: a pointer to the variable that the file should read to and write
* from.
@ -236,11 +232,11 @@ static const struct file_operations fops_bool = {
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
@ -264,13 +260,11 @@ static struct file_operations fops_blob = {
};
/**
* debugfs_create_blob - create a file in the debugfs filesystem that is
* used to read and write a binary blob.
*
* debugfs_create_blob - create a debugfs file that is used to read and write a binary blob
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this paramater is NULL, then the
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
* @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer
* to the blob data and the size of the data.
@ -282,11 +276,11 @@ static struct file_operations fops_blob = {
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_blob(const char *name, mode_t mode,

View File

@ -162,7 +162,6 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
/**
* debugfs_create_file - create a file in the debugfs filesystem
*
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@ -182,11 +181,11 @@ static int debugfs_create_by_name(const char *name, mode_t mode,
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_file(const char *name, mode_t mode,
@ -221,7 +220,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_file);
/**
* debugfs_create_dir - create a directory in the debugfs filesystem
*
* @name: a pointer to a string containing the name of the directory to
* create.
* @parent: a pointer to the parent dentry for this file. This should be a
@ -233,11 +231,11 @@ EXPORT_SYMBOL_GPL(debugfs_create_file);
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
* you are responsible here.) If an error occurs, NULL will be returned.
* you are responsible here.) If an error occurs, %NULL will be returned.
*
* If debugfs is not enabled in the kernel, the value -ENODEV will be
* If debugfs is not enabled in the kernel, the value -%ENODEV will be
* returned. It is not wise to check for this value, but rather, check for
* NULL or !NULL instead as to eliminate the need for #ifdef in the calling
* %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
* code.
*/
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
@ -250,7 +248,6 @@ EXPORT_SYMBOL_GPL(debugfs_create_dir);
/**
* debugfs_remove - removes a file or directory from the debugfs filesystem
*
* @dentry: a pointer to a the dentry of the file or directory to be
* removed.
*

View File

@ -17,6 +17,7 @@
#include <linux/acct.h>
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/seq_file.h>
#include <linux/namespace.h>
#include <linux/namei.h>
@ -28,15 +29,6 @@
extern int __init init_rootfs(void);
#ifdef CONFIG_SYSFS
extern int __init sysfs_init(void);
#else
static inline int sysfs_init(void)
{
return 0;
}
#endif
/* spinlock for vfsmount related operations, inplace of dcache_lock */
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);

View File

@ -10,6 +10,7 @@
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/slab.h>
@ -176,7 +177,6 @@ const struct file_operations bin_fops = {
* sysfs_create_bin_file - create binary file for object.
* @kobj: object.
* @attr: attribute descriptor.
*
*/
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
@ -191,13 +191,16 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
* sysfs_remove_bin_file - remove binary file for object.
* @kobj: object.
* @attr: attribute descriptor.
*
*/
int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
{
sysfs_hash_and_remove(kobj->dentry,attr->attr.name);
return 0;
if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
printk(KERN_ERR "%s: "
"bad dentry or inode or no such file: \"%s\"\n",
__FUNCTION__, attr->attr.name);
dump_stack();
}
}
EXPORT_SYMBOL_GPL(sysfs_create_bin_file);

View File

@ -43,7 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd,
memset(sd, 0, sizeof(*sd));
atomic_set(&sd->s_count, 1);
atomic_set(&sd->s_event, 0);
atomic_set(&sd->s_event, 1);
INIT_LIST_HEAD(&sd->s_children);
list_add(&sd->s_sibling, &parent_sd->s_children);
sd->s_element = element;

View File

@ -12,6 +12,7 @@
#include <linux/namei.h>
#include <linux/backing-dev.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include "sysfs.h"
extern struct super_block * sysfs_sb;
@ -234,17 +235,18 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent)
}
}
void sysfs_hash_and_remove(struct dentry * dir, const char * name)
int sysfs_hash_and_remove(struct dentry * dir, const char * name)
{
struct sysfs_dirent * sd;
struct sysfs_dirent * parent_sd;
int found = 0;
if (!dir)
return;
return -ENOENT;
if (dir->d_inode == NULL)
/* no inode means this hasn't been made visible yet */
return;
return -ENOENT;
parent_sd = dir->d_fsdata;
mutex_lock(&dir->d_inode->i_mutex);
@ -255,8 +257,11 @@ void sysfs_hash_and_remove(struct dentry * dir, const char * name)
list_del_init(&sd->s_sibling);
sysfs_drop_dentry(sd, dir);
sysfs_put(sd);
found = 1;
break;
}
}
mutex_unlock(&dir->d_inode->i_mutex);
return found ? 0 : -ENOENT;
}

View File

@ -3,6 +3,7 @@
*/
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/namei.h>
@ -82,10 +83,19 @@ exit1:
*/
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
{
struct dentry * dentry = kobj->dentry;
struct dentry *dentry = NULL;
int error = -EEXIST;
BUG_ON(!kobj || !kobj->dentry || !name);
BUG_ON(!name);
if (!kobj) {
if (sysfs_mount && sysfs_mount->mnt_sb)
dentry = sysfs_mount->mnt_sb->s_root;
} else
dentry = kobj->dentry;
if (!dentry)
return -EFAULT;
mutex_lock(&dentry->d_inode->i_mutex);
if (!sysfs_dirent_exist(dentry->d_fsdata, name))

View File

@ -10,7 +10,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *,
umode_t, int);
extern int sysfs_add_file(struct dentry *, const struct attribute *, int);
extern void sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern int sysfs_hash_and_remove(struct dentry * dir, const char * name);
extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);

View File

@ -99,6 +99,11 @@ extern void __chk_io_ptr(void __iomem *);
#define __must_check
#endif
#ifndef CONFIG_ENABLE_MUST_CHECK
#undef __must_check
#define __must_check
#endif
/*
* Allow us to avoid 'defined but not used' warnings on functions and data,
* as well as force them to be emitted to the assembly file.

View File

@ -15,6 +15,7 @@
#include <linux/kobject.h>
#include <linux/klist.h>
#include <linux/list.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pm.h>
@ -51,14 +52,17 @@ struct bus_type {
int (*probe)(struct device * dev);
int (*remove)(struct device * dev);
void (*shutdown)(struct device * dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*suspend_late)(struct device * dev, pm_message_t state);
int (*resume_early)(struct device * dev);
int (*resume)(struct device * dev);
};
extern int bus_register(struct bus_type * bus);
extern int __must_check bus_register(struct bus_type * bus);
extern void bus_unregister(struct bus_type * bus);
extern void bus_rescan_devices(struct bus_type * bus);
extern int __must_check bus_rescan_devices(struct bus_type * bus);
/* iterator helpers for buses */
@ -67,9 +71,9 @@ int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data,
struct device * bus_find_device(struct bus_type *bus, struct device *start,
void *data, int (*match)(struct device *, void *));
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
void * data, int (*fn)(struct device_driver *, void *));
int __must_check bus_for_each_drv(struct bus_type *bus,
struct device_driver *start, void *data,
int (*fn)(struct device_driver *, void *));
/* driverfs interface for exporting bus attributes */
@ -82,7 +86,8 @@ struct bus_attribute {
#define BUS_ATTR(_name,_mode,_show,_store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store)
extern int bus_create_file(struct bus_type *, struct bus_attribute *);
extern int __must_check bus_create_file(struct bus_type *,
struct bus_attribute *);
extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
struct device_driver {
@ -101,16 +106,18 @@ struct device_driver {
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, pm_message_t state);
int (*resume) (struct device * dev);
unsigned int multithread_probe:1;
};
extern int driver_register(struct device_driver * drv);
extern int __must_check driver_register(struct device_driver * drv);
extern void driver_unregister(struct device_driver * drv);
extern struct device_driver * get_driver(struct device_driver * drv);
extern void put_driver(struct device_driver * drv);
extern struct device_driver *driver_find(const char *name, struct bus_type *bus);
extern int driver_probe_done(void);
/* driverfs interface for exporting driver attributes */
@ -123,16 +130,17 @@ struct driver_attribute {
#define DRIVER_ATTR(_name,_mode,_show,_store) \
struct driver_attribute driver_attr_##_name = __ATTR(_name,_mode,_show,_store)
extern int driver_create_file(struct device_driver *, struct driver_attribute *);
extern int __must_check driver_create_file(struct device_driver *,
struct driver_attribute *);
extern void driver_remove_file(struct device_driver *, struct driver_attribute *);
extern int driver_for_each_device(struct device_driver * drv, struct device * start,
void * data, int (*fn)(struct device *, void *));
extern int __must_check driver_for_each_device(struct device_driver * drv,
struct device *start, void *data,
int (*fn)(struct device *, void *));
struct device * driver_find_device(struct device_driver *drv,
struct device *start, void *data,
int (*match)(struct device *, void *));
/*
* device classes
*/
@ -146,17 +154,26 @@ struct class {
struct list_head interfaces;
struct semaphore sem; /* locks both the children and interfaces lists */
struct kobject *virtual_dir;
struct class_attribute * class_attrs;
struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs;
int (*uevent)(struct class_device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
int (*dev_uevent)(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
};
extern int class_register(struct class *);
extern int __must_check class_register(struct class *);
extern void class_unregister(struct class *);
@ -169,7 +186,8 @@ struct class_attribute {
#define CLASS_ATTR(_name,_mode,_show,_store) \
struct class_attribute class_attr_##_name = __ATTR(_name,_mode,_show,_store)
extern int class_create_file(struct class *, const struct class_attribute *);
extern int __must_check class_create_file(struct class *,
const struct class_attribute *);
extern void class_remove_file(struct class *, const struct class_attribute *);
struct class_device_attribute {
@ -182,7 +200,7 @@ struct class_device_attribute {
struct class_device_attribute class_device_attr_##_name = \
__ATTR(_name,_mode,_show,_store)
extern int class_device_create_file(struct class_device *,
extern int __must_check class_device_create_file(struct class_device *,
const struct class_device_attribute *);
/**
@ -242,10 +260,10 @@ class_set_devdata (struct class_device *dev, void *data)
}
extern int class_device_register(struct class_device *);
extern int __must_check class_device_register(struct class_device *);
extern void class_device_unregister(struct class_device *);
extern void class_device_initialize(struct class_device *);
extern int class_device_add(struct class_device *);
extern int __must_check class_device_add(struct class_device *);
extern void class_device_del(struct class_device *);
extern int class_device_rename(struct class_device *, char *);
@ -255,7 +273,7 @@ extern void class_device_put(struct class_device *);
extern void class_device_remove_file(struct class_device *,
const struct class_device_attribute *);
extern int class_device_create_bin_file(struct class_device *,
extern int __must_check class_device_create_bin_file(struct class_device *,
struct bin_attribute *);
extern void class_device_remove_bin_file(struct class_device *,
struct bin_attribute *);
@ -266,22 +284,23 @@ struct class_interface {
int (*add) (struct class_device *, struct class_interface *);
void (*remove) (struct class_device *, struct class_interface *);
int (*add_dev) (struct device *, struct class_interface *);
void (*remove_dev) (struct device *, struct class_interface *);
};
extern int class_interface_register(struct class_interface *);
extern int __must_check class_interface_register(struct class_interface *);
extern void class_interface_unregister(struct class_interface *);
extern struct class *class_create(struct module *owner, char *name);
extern struct class *class_create(struct module *owner, const char *name);
extern void class_destroy(struct class *cls);
extern struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
struct device *device,
char *fmt, ...)
const char *fmt, ...)
__attribute__((format(printf,5,6)));
extern void class_device_destroy(struct class *cls, dev_t devt);
/* interface for exporting device attributes */
struct device_attribute {
struct attribute attr;
@ -294,8 +313,13 @@ struct device_attribute {
#define DEVICE_ATTR(_name,_mode,_show,_store) \
struct device_attribute dev_attr_##_name = __ATTR(_name,_mode,_show,_store)
extern int device_create_file(struct device *device, struct device_attribute * entry);
extern int __must_check device_create_file(struct device *device,
struct device_attribute * entry);
extern void device_remove_file(struct device * dev, struct device_attribute * attr);
extern int __must_check device_create_bin_file(struct device *dev,
struct bin_attribute *attr);
extern void device_remove_bin_file(struct device *dev,
struct bin_attribute *attr);
struct device {
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
@ -305,6 +329,7 @@ struct device {
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
unsigned is_registered:1;
struct device_attribute uevent_attr;
struct device_attribute *devt_attr;
@ -338,6 +363,7 @@ struct device {
struct list_head node;
struct class *class; /* optional*/
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
void (*release)(struct device * dev);
};
@ -356,38 +382,41 @@ dev_set_drvdata (struct device *dev, void *data)
static inline int device_is_registered(struct device *dev)
{
return klist_node_attached(&dev->knode_bus);
return dev->is_registered;
}
/*
* High level routines for use by the bus drivers
*/
extern int device_register(struct device * dev);
extern int __must_check device_register(struct device * dev);
extern void device_unregister(struct device * dev);
extern void device_initialize(struct device * dev);
extern int device_add(struct device * dev);
extern int __must_check device_add(struct device * dev);
extern void device_del(struct device * dev);
extern int device_for_each_child(struct device *, void *,
extern int __must_check device_for_each_child(struct device *, void *,
int (*fn)(struct device *, void *));
extern int device_rename(struct device *dev, char *new_name);
/*
* Manual binding of a device to driver. See drivers/base/bus.c
* for information on use.
*/
extern void device_bind_driver(struct device * dev);
extern int __must_check device_bind_driver(struct device *dev);
extern void device_release_driver(struct device * dev);
extern int device_attach(struct device * dev);
extern void driver_attach(struct device_driver * drv);
extern void device_reprobe(struct device *dev);
extern int __must_check device_attach(struct device * dev);
extern int __must_check driver_attach(struct device_driver *drv);
extern int __must_check device_reprobe(struct device *dev);
/*
* Easy functions for dynamically creating devices on the fly
*/
extern struct device *device_create(struct class *cls, struct device *parent,
dev_t devt, char *fmt, ...)
dev_t devt, const char *fmt, ...)
__attribute__((format(printf,4,5)));
extern void device_destroy(struct class *cls, dev_t devt);
extern int virtual_device_parent(struct device *dev);
/*
* Platform "fixup" functions - allow the platform to have their say
* about devices and actions that the general device layer doesn't
@ -412,7 +441,7 @@ extern void device_shutdown(void);
/* drivers/base/firmware.c */
extern int firmware_register(struct subsystem *);
extern int __must_check firmware_register(struct subsystem *);
extern void firmware_unregister(struct subsystem *);
/* debugging and troubleshooting/diagnostic helpers. */

View File

@ -20,6 +20,7 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/compiler.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
#include <linux/kref.h>
@ -71,12 +72,12 @@ static inline const char * kobject_name(const struct kobject * kobj)
extern void kobject_init(struct kobject *);
extern void kobject_cleanup(struct kobject *);
extern int kobject_add(struct kobject *);
extern int __must_check kobject_add(struct kobject *);
extern void kobject_del(struct kobject *);
extern int kobject_rename(struct kobject *, const char *new_name);
extern int __must_check kobject_rename(struct kobject *, const char *new_name);
extern int kobject_register(struct kobject *);
extern int __must_check kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *);
@ -128,8 +129,8 @@ struct kset {
extern void kset_init(struct kset * k);
extern int kset_add(struct kset * k);
extern int kset_register(struct kset * k);
extern int __must_check kset_add(struct kset * k);
extern int __must_check kset_register(struct kset * k);
extern void kset_unregister(struct kset * k);
static inline struct kset * to_kset(struct kobject * kobj)
@ -239,7 +240,7 @@ extern struct subsystem hypervisor_subsys;
(obj)->subsys.kset.kobj.kset = &(_subsys).kset
extern void subsystem_init(struct subsystem *);
extern int subsystem_register(struct subsystem *);
extern int __must_check subsystem_register(struct subsystem *);
extern void subsystem_unregister(struct subsystem *);
static inline struct subsystem * subsys_get(struct subsystem * s)
@ -258,7 +259,8 @@ struct subsys_attribute {
ssize_t (*store)(struct subsystem *, const char *, size_t);
};
extern int subsys_create_file(struct subsystem * , struct subsys_attribute *);
extern int __must_check subsys_create_file(struct subsystem * ,
struct subsys_attribute *);
#if defined(CONFIG_HOTPLUG)
void kobject_uevent(struct kobject *kobj, enum kobject_action action);

View File

@ -49,6 +49,7 @@
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/compiler.h>
#include <linux/errno.h>
#include <linux/device.h>
@ -346,6 +347,8 @@ struct pci_driver {
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
void (*shutdown) (struct pci_dev *dev);
@ -401,7 +404,7 @@ extern struct list_head pci_root_buses; /* list of all known PCI buses */
extern struct list_head pci_devices; /* list of all devices */
void pcibios_fixup_bus(struct pci_bus *);
int pcibios_enable_device(struct pci_dev *, int mask);
int __must_check pcibios_enable_device(struct pci_dev *, int mask);
char *pcibios_setup (char *str);
/* Used only when drivers/pci/setup.c is used */
@ -488,19 +491,19 @@ static inline int pci_write_config_dword(struct pci_dev *dev, int where, u32 val
return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val);
}
int pci_enable_device(struct pci_dev *dev);
int pci_enable_device_bars(struct pci_dev *dev, int mask);
int __must_check pci_enable_device(struct pci_dev *dev);
int __must_check pci_enable_device_bars(struct pci_dev *dev, int mask);
void pci_disable_device(struct pci_dev *dev);
void pci_set_master(struct pci_dev *dev);
#define HAVE_PCI_SET_MWI
int pci_set_mwi(struct pci_dev *dev);
int __must_check pci_set_mwi(struct pci_dev *dev);
void pci_clear_mwi(struct pci_dev *dev);
void pci_intx(struct pci_dev *dev, int enable);
int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
int pci_assign_resource(struct pci_dev *dev, int i);
int pci_assign_resource_fixed(struct pci_dev *dev, int i);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int __must_check pci_assign_resource_fixed(struct pci_dev *dev, int i);
void pci_restore_bars(struct pci_dev *dev);
/* ROM control related routines */
@ -526,23 +529,24 @@ void pdev_sort_resources(struct pci_dev *, struct resource_list *);
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
int (*)(struct pci_dev *, u8, u8));
#define HAVE_PCI_REQ_REGIONS 2
int pci_request_regions(struct pci_dev *, const char *);
int __must_check pci_request_regions(struct pci_dev *, const char *);
void pci_release_regions(struct pci_dev *);
int pci_request_region(struct pci_dev *, int, const char *);
int __must_check pci_request_region(struct pci_dev *, int, const char *);
void pci_release_region(struct pci_dev *, int);
/* drivers/pci/bus.c */
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
resource_size_t min, unsigned int type_mask,
void (*alignf)(void *, struct resource *,
resource_size_t, resource_size_t),
void *alignf_data);
int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
struct resource *res, resource_size_t size,
resource_size_t align, resource_size_t min,
unsigned int type_mask,
void (*alignf)(void *, struct resource *,
resource_size_t, resource_size_t),
void *alignf_data);
void pci_enable_bridges(struct pci_bus *bus);
/* Proper probing supporting hot-pluggable devices */
int __pci_register_driver(struct pci_driver *, struct module *);
static inline int pci_register_driver(struct pci_driver *driver)
int __must_check __pci_register_driver(struct pci_driver *, struct module *);
static inline int __must_check pci_register_driver(struct pci_driver *driver)
{
return __pci_register_driver(driver, THIS_MODULE);
}

View File

@ -49,6 +49,8 @@ struct platform_driver {
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};

View File

@ -142,29 +142,61 @@ typedef struct pm_message {
} pm_message_t;
/*
* There are 4 important states driver can be in:
* ON -- driver is working
* FREEZE -- stop operations and apply whatever policy is applicable to a
* suspended driver of that class, freeze queues for block like IDE
* does, drop packets for ethernet, etc... stop DMA engine too etc...
* so a consistent image can be saved; but do not power any hardware
* down.
* SUSPEND - like FREEZE, but hardware is doing as much powersaving as
* possible. Roughly pci D3.
* Several driver power state transitions are externally visible, affecting
* the state of pending I/O queues and (for drivers that touch hardware)
* interrupts, wakeups, DMA, and other hardware state. There may also be
* internal transitions to various low power modes, which are transparent
* to the rest of the driver stack (such as a driver that's ON gating off
* clocks which are not in active use).
*
* Unfortunately, current drivers only recognize numeric values 0 (ON) and 3
* (SUSPEND). We'll need to fix the drivers. So yes, putting 3 to all different
* defines is intentional, and will go away as soon as drivers are fixed. Also
* note that typedef is neccessary, we'll probably want to switch to
* typedef struct pm_message_t { int event; int flags; } pm_message_t
* or something similar soon.
* One transition is triggered by resume(), after a suspend() call; the
* message is implicit:
*
* ON Driver starts working again, responding to hardware events
* and software requests. The hardware may have gone through
* a power-off reset, or it may have maintained state from the
* previous suspend() which the driver will rely on while
* resuming. On most platforms, there are no restrictions on
* availability of resources like clocks during resume().
*
* Other transitions are triggered by messages sent using suspend(). All
* these transitions quiesce the driver, so that I/O queues are inactive.
* That commonly entails turning off IRQs and DMA; there may be rules
* about how to quiesce that are specific to the bus or the device's type.
* (For example, network drivers mark the link state.) Other details may
* differ according to the message:
*
* SUSPEND Quiesce, enter a low power device state appropriate for
* the upcoming system state (such as PCI_D3hot), and enable
* wakeup events as appropriate.
*
* FREEZE Quiesce operations so that a consistent image can be saved;
* but do NOT otherwise enter a low power device state, and do
* NOT emit system wakeup events.
*
* PRETHAW Quiesce as if for FREEZE; additionally, prepare for restoring
* the system from a snapshot taken after an earlier FREEZE.
* Some drivers will need to reset their hardware state instead
* of preserving it, to ensure that it's never mistaken for the
* state which that earlier snapshot had set up.
*
* A minimally power-aware driver treats all messages as SUSPEND, fully
* reinitializes its device during resume() -- whether or not it was reset
* during the suspend/resume cycle -- and can't issue wakeup events.
*
* More power-aware drivers may also use low power states at runtime as
* well as during system sleep states like PM_SUSPEND_STANDBY. They may
* be able to use wakeup events to exit from runtime low-power states,
* or from system low-power states such as standby or suspend-to-RAM.
*/
#define PM_EVENT_ON 0
#define PM_EVENT_FREEZE 1
#define PM_EVENT_SUSPEND 2
#define PM_EVENT_PRETHAW 3
#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
@ -190,6 +222,7 @@ extern void device_resume(void);
extern suspend_disk_method_t pm_disk_mode;
extern int device_suspend(pm_message_t state);
extern int device_prepare_suspend(pm_message_t state);
#define device_set_wakeup_enable(dev,val) \
((dev)->power.should_wakeup = !!(val))

View File

@ -10,6 +10,7 @@
#ifndef _SYSFS_H_
#define _SYSFS_H_
#include <linux/compiler.h>
#include <asm/atomic.h>
struct kobject;
@ -86,40 +87,44 @@ struct sysfs_dirent {
#ifdef CONFIG_SYSFS
extern int
extern int __must_check
sysfs_create_dir(struct kobject *);
extern void
sysfs_remove_dir(struct kobject *);
extern int
extern int __must_check
sysfs_rename_dir(struct kobject *, const char *new_name);
extern int
extern int __must_check
sysfs_create_file(struct kobject *, const struct attribute *);
extern int
extern int __must_check
sysfs_update_file(struct kobject *, const struct attribute *);
extern int
extern int __must_check
sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode);
extern void
sysfs_remove_file(struct kobject *, const struct attribute *);
extern int
extern int __must_check
sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name);
extern void
sysfs_remove_link(struct kobject *, const char * name);
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr);
int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr);
int __must_check sysfs_create_bin_file(struct kobject *kobj,
struct bin_attribute *attr);
void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);
int sysfs_create_group(struct kobject *, const struct attribute_group *);
int __must_check sysfs_create_group(struct kobject *,
const struct attribute_group *);
void sysfs_remove_group(struct kobject *, const struct attribute_group *);
void sysfs_notify(struct kobject * k, char *dir, char *attr);
extern int __must_check sysfs_init(void);
#else /* CONFIG_SYSFS */
static inline int sysfs_create_dir(struct kobject * k)
@ -191,6 +196,11 @@ static inline void sysfs_notify(struct kobject * k, char *dir, char *attr)
{
}
static inline int __must_check sysfs_init(void)
{
return 0;
}
#endif /* CONFIG_SYSFS */
#endif /* _SYSFS_H_ */

View File

@ -341,7 +341,7 @@ extern int video_usercopy(struct inode *inode, struct file *file,
extern struct video_device* video_devdata(struct file*);
#define to_video_device(cd) container_of(cd, struct video_device, class_dev)
static inline int
static inline int __must_check
video_device_create_file(struct video_device *vfd,
struct class_device_attribute *attr)
{

View File

@ -8,6 +8,7 @@
#include <linux/security.h>
#include <linux/delay.h>
#include <linux/mount.h>
#include <linux/device.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_fs_sb.h>
@ -403,6 +404,10 @@ void __init prepare_namespace(void)
ssleep(root_delay);
}
/* wait for the known devices to complete their probing */
while (driver_probe_done() != 0)
msleep(100);
md_run_setup();
if (saved_root_name[0]) {

View File

@ -64,6 +64,17 @@ config PM_TRACE
CAUTION: this option will cause your machine's real-time clock to be
set to an invalid time after a resume.
config PM_SYSFS_DEPRECATED
bool "Driver model /sys/devices/.../power/state files (DEPRECATED)"
depends on PM && SYSFS
default n
help
The driver model started out with a sysfs file intended to provide
a userspace hook for device power management. This feature has never
worked very well, except for limited testing purposes, and so it will
be removed. It's not clear that a generic mechanism could really
handle the wide variability of device power states; any replacements
are likely to be bus or driver specific.
config SOFTWARE_SUSPEND
bool "Software Suspend"

View File

@ -103,7 +103,7 @@ static void unprepare_processes(void)
}
/**
* pm_suspend_disk - The granpappy of power management.
* pm_suspend_disk - The granpappy of hibernation power management.
*
* If we're going through the firmware, then get it over with quickly.
*
@ -212,7 +212,7 @@ static int software_resume(void)
pr_debug("PM: Preparing devices for restore.\n");
if ((error = device_suspend(PMSG_FREEZE))) {
if ((error = device_suspend(PMSG_PRETHAW))) {
printk("Some devices failed to suspend\n");
swsusp_free();
goto Thaw;

View File

@ -247,6 +247,9 @@ int swsusp_suspend(void)
restore_processor_state();
Restore_highmem:
restore_highmem();
/* NOTE: device_power_up() is just a resume() for devices
* that suspended with irqs off ... no overall powerup.
*/
device_power_up();
Enable_irqs:
local_irq_enable();
@ -256,8 +259,12 @@ Enable_irqs:
int swsusp_resume(void)
{
int error;
local_irq_disable();
if (device_power_down(PMSG_FREEZE))
/* NOTE: device_power_down() is just a suspend() with irqs off;
* it has no special "power things down" semantics
*/
if (device_power_down(PMSG_PRETHAW))
printk(KERN_ERR "Some devices failed to power down, very bad\n");
/* We'll ignore saved state, but this gets preempt count (etc) right */
save_processor_state();

View File

@ -196,7 +196,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
snapshot_free_unused_memory(&data->handle);
down(&pm_sem);
pm_prepare_console();
error = device_suspend(PMSG_FREEZE);
error = device_suspend(PMSG_PRETHAW);
if (!error) {
error = swsusp_resume();
device_resume();

View File

@ -8,6 +8,13 @@ config PRINTK_TIME
operations. This is useful for identifying long delays
in kernel startup.
config ENABLE_MUST_CHECK
bool "Enable __must_check logic"
default y
help
Enable the __must_check logic in the kernel build. Disable this to
suppress the "warning: ignoring return value of 'foo', declared with
attribute warn_unused_result" messages.
config MAGIC_SYSRQ
bool "Magic SysRq key"

View File

@ -123,12 +123,10 @@ EXPORT_SYMBOL_GPL(klist_add_tail);
static void klist_release(struct kref * kref)
{
struct klist_node * n = container_of(kref, struct klist_node, n_ref);
void (*put)(struct klist_node *) = n->n_klist->put;
list_del(&n->n_node);
complete(&n->n_removed);
n->n_klist = NULL;
if (put)
put(n);
}
static int klist_dec_and_del(struct klist_node * n)
@ -145,10 +143,14 @@ static int klist_dec_and_del(struct klist_node * n)
void klist_del(struct klist_node * n)
{
struct klist * k = n->n_klist;
void (*put)(struct klist_node *) = k->put;
spin_lock(&k->k_lock);
klist_dec_and_del(n);
if (!klist_dec_and_del(n))
put = NULL;
spin_unlock(&k->k_lock);
if (put)
put(n);
}
EXPORT_SYMBOL_GPL(klist_del);
@ -161,10 +163,7 @@ EXPORT_SYMBOL_GPL(klist_del);
void klist_remove(struct klist_node * n)
{
struct klist * k = n->n_klist;
spin_lock(&k->k_lock);
klist_dec_and_del(n);
spin_unlock(&k->k_lock);
klist_del(n);
wait_for_completion(&n->n_removed);
}
@ -260,12 +259,15 @@ static struct klist_node * to_klist_node(struct list_head * n)
struct klist_node * klist_next(struct klist_iter * i)
{
struct list_head * next;
struct klist_node * lnode = i->i_cur;
struct klist_node * knode = NULL;
void (*put)(struct klist_node *) = i->i_klist->put;
spin_lock(&i->i_klist->k_lock);
if (i->i_cur) {
next = i->i_cur->n_node.next;
klist_dec_and_del(i->i_cur);
if (lnode) {
next = lnode->n_node.next;
if (!klist_dec_and_del(lnode))
put = NULL;
} else
next = i->i_head->next;
@ -275,6 +277,8 @@ struct klist_node * klist_next(struct klist_iter * i)
}
i->i_cur = knode;
spin_unlock(&i->i_klist->k_lock);
if (put && lnode)
put(lnode);
return knode;
}

View File

@ -407,6 +407,7 @@ static struct kobj_type dir_ktype = {
struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
{
struct kobject *k;
int ret;
if (!parent)
return NULL;
@ -418,7 +419,13 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
k->parent = parent;
k->ktype = &dir_ktype;
kobject_set_name(k, name);
kobject_register(k);
ret = kobject_register(k);
if (ret < 0) {
printk(KERN_WARNING "kobject_add_dir: "
"kobject_register error: %d\n", ret);
kobject_del(k);
return NULL;
}
return k;
}