mirror of
https://gitee.com/openharmony/kernel_linux
synced 2025-05-13 18:26:55 +00:00
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6: (47 commits) Driver core: Don't call put methods while holding a spinlock Driver core: Remove unneeded routines from driver core Driver core: Fix potential deadlock in driver core PCI: enable driver multi-threaded probe Driver Core: add ability for drivers to do a threaded probe sysfs: add proper sysfs_init() prototype drivers/base: check errors drivers/base: Platform notify needs to occur before drivers attach to the device v4l-dev2: handle __must_check add CONFIG_ENABLE_MUST_CHECK add __must_check to device management code Driver core: fixed add_bind_files() definition Driver core: fix comments in drivers/base/power/resume.c sysfs_remove_bin_file: no return value, dump_stack on error kobject: must_check fixes Driver core: add ability for devices to create and remove bin files Class: add support for class interfaces for devices Driver core: create devices/virtual/ tree Driver core: add device_rename function Driver core: add ability for classes to handle devices properly ...
This commit is contained in:
commit
dd77a4ee0f
@ -1,13 +1,12 @@
|
|||||||
What: devfs
|
What: devfs
|
||||||
Date: July 2005
|
Date: July 2005 (scheduled), finally removed in kernel v2.6.18
|
||||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||||
Description:
|
Description:
|
||||||
devfs has been unmaintained for a number of years, has unfixable
|
devfs has been unmaintained for a number of years, has unfixable
|
||||||
races, contains a naming policy within the kernel that is
|
races, contains a naming policy within the kernel that is
|
||||||
against the LSB, and can be replaced by using udev.
|
against the LSB, and can be replaced by using udev.
|
||||||
The files fs/devfs/*, include/linux/devfs_fs*.h will be removed,
|
The files fs/devfs/*, include/linux/devfs_fs*.h were removed,
|
||||||
along with the the assorted devfs function calls throughout the
|
along with the the assorted devfs function calls throughout the
|
||||||
kernel tree.
|
kernel tree.
|
||||||
|
|
||||||
Users:
|
Users:
|
||||||
|
|
88
Documentation/ABI/testing/sysfs-power
Normal file
88
Documentation/ABI/testing/sysfs-power
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
What: /sys/power/
|
||||||
|
Date: August 2006
|
||||||
|
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||||
|
Description:
|
||||||
|
The /sys/power directory will contain files that will
|
||||||
|
provide a unified interface to the power management
|
||||||
|
subsystem.
|
||||||
|
|
||||||
|
What: /sys/power/state
|
||||||
|
Date: August 2006
|
||||||
|
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||||
|
Description:
|
||||||
|
The /sys/power/state file controls the system power state.
|
||||||
|
Reading from this file returns what states are supported,
|
||||||
|
which is hard-coded to 'standby' (Power-On Suspend), 'mem'
|
||||||
|
(Suspend-to-RAM), and 'disk' (Suspend-to-Disk).
|
||||||
|
|
||||||
|
Writing to this file one of these strings causes the system to
|
||||||
|
transition into that state. Please see the file
|
||||||
|
Documentation/power/states.txt for a description of each of
|
||||||
|
these states.
|
||||||
|
|
||||||
|
What: /sys/power/disk
|
||||||
|
Date: August 2006
|
||||||
|
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||||
|
Description:
|
||||||
|
The /sys/power/disk file controls the operating mode of the
|
||||||
|
suspend-to-disk mechanism. Reading from this file returns
|
||||||
|
the name of the method by which the system will be put to
|
||||||
|
sleep on the next suspend. There are four methods supported:
|
||||||
|
'firmware' - means that the memory image will be saved to disk
|
||||||
|
by some firmware, in which case we also assume that the
|
||||||
|
firmware will handle the system suspend.
|
||||||
|
'platform' - the memory image will be saved by the kernel and
|
||||||
|
the system will be put to sleep by the platform driver (e.g.
|
||||||
|
ACPI or other PM registers).
|
||||||
|
'shutdown' - the memory image will be saved by the kernel and
|
||||||
|
the system will be powered off.
|
||||||
|
'reboot' - the memory image will be saved by the kernel and
|
||||||
|
the system will be rebooted.
|
||||||
|
|
||||||
|
The suspend-to-disk method may be chosen by writing to this
|
||||||
|
file one of the accepted strings:
|
||||||
|
|
||||||
|
'firmware'
|
||||||
|
'platform'
|
||||||
|
'shutdown'
|
||||||
|
'reboot'
|
||||||
|
|
||||||
|
It will only change to 'firmware' or 'platform' if the system
|
||||||
|
supports that.
|
||||||
|
|
||||||
|
What: /sys/power/image_size
|
||||||
|
Date: August 2006
|
||||||
|
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||||
|
Description:
|
||||||
|
The /sys/power/image_size file controls the size of the image
|
||||||
|
created by the suspend-to-disk mechanism. It can be written a
|
||||||
|
string representing a non-negative integer that will be used
|
||||||
|
as an upper limit of the image size, in bytes. The kernel's
|
||||||
|
suspend-to-disk code will do its best to ensure the image size
|
||||||
|
will not exceed this number. However, if it turns out to be
|
||||||
|
impossible, the kernel will try to suspend anyway using the
|
||||||
|
smallest image possible. In particular, if "0" is written to
|
||||||
|
this file, the suspend image will be as small as possible.
|
||||||
|
|
||||||
|
Reading from this file will display the current image size
|
||||||
|
limit, which is set to 500 MB by default.
|
||||||
|
|
||||||
|
What: /sys/power/pm_trace
|
||||||
|
Date: August 2006
|
||||||
|
Contact: Rafael J. Wysocki <rjw@sisk.pl>
|
||||||
|
Description:
|
||||||
|
The /sys/power/pm_trace file controls the code which saves the
|
||||||
|
last PM event point in the RTC across reboots, so that you can
|
||||||
|
debug a machine that just hangs during suspend (or more
|
||||||
|
commonly, during resume). Namely, the RTC is only used to save
|
||||||
|
the last PM event point if this file contains '1'. Initially
|
||||||
|
it contains '0' which may be changed to '1' by writing a
|
||||||
|
string representing a nonzero integer into it.
|
||||||
|
|
||||||
|
To use this debugging feature you should attempt to suspend
|
||||||
|
the machine, then reboot it and run
|
||||||
|
|
||||||
|
dmesg -s 1000000 | grep 'hash matches'
|
||||||
|
|
||||||
|
CAUTION: Using it will cause your machine's real-time (CMOS)
|
||||||
|
clock to be set to a random invalid time after a resume.
|
@ -6,6 +6,21 @@ be removed from this file.
|
|||||||
|
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
What: /sys/devices/.../power/state
|
||||||
|
dev->power.power_state
|
||||||
|
dpm_runtime_{suspend,resume)()
|
||||||
|
When: July 2007
|
||||||
|
Why: Broken design for runtime control over driver power states, confusing
|
||||||
|
driver-internal runtime power management with: mechanisms to support
|
||||||
|
system-wide sleep state transitions; event codes that distinguish
|
||||||
|
different phases of swsusp "sleep" transitions; and userspace policy
|
||||||
|
inputs. This framework was never widely used, and most attempts to
|
||||||
|
use it were broken. Drivers should instead be exposing domain-specific
|
||||||
|
interfaces either to kernel or to userspace.
|
||||||
|
Who: Pavel Machek <pavel@suse.cz>
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
|
||||||
What: RAW driver (CONFIG_RAW_DRIVER)
|
What: RAW driver (CONFIG_RAW_DRIVER)
|
||||||
When: December 2005
|
When: December 2005
|
||||||
Why: declared obsolete since kernel 2.6.3
|
Why: declared obsolete since kernel 2.6.3
|
||||||
@ -294,3 +309,15 @@ Why: The frame diverter is included in most distribution kernels, but is
|
|||||||
It is not clear if anyone is still using it.
|
It is not clear if anyone is still using it.
|
||||||
Who: Stephen Hemminger <shemminger@osdl.org>
|
Who: Stephen Hemminger <shemminger@osdl.org>
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
|
||||||
|
What: PHYSDEVPATH, PHYSDEVBUS, PHYSDEVDRIVER in the uevent environment
|
||||||
|
When: Oktober 2008
|
||||||
|
Why: The stacking of class devices makes these values misleading and
|
||||||
|
inconsistent.
|
||||||
|
Class devices should not carry any of these properties, and bus
|
||||||
|
devices have SUBSYTEM and DRIVER as a replacement.
|
||||||
|
Who: Kay Sievers <kay.sievers@suse.de>
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
@ -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
|
Two Models for Device Power Management
|
||||||
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
|
Drivers will use one or both of these models to put devices into low-power
|
||||||
a low-power state while the system is running (and independently of
|
states:
|
||||||
any other power management activity).
|
|
||||||
|
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 {
|
struct bus_type {
|
||||||
...
|
...
|
||||||
int (*suspend)(struct device * dev, pm_message_t state);
|
int (*suspend)(struct device *dev, pm_message_t state);
|
||||||
int (*resume)(struct device * dev);
|
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
|
Bus drivers implement those methods as appropriate for the hardware and
|
||||||
the call into a bus-specific request and forwarding the call to the
|
the drivers using it; PCI works differently from USB, and so on. Not many
|
||||||
bus-specific drivers. For example, PCI drivers implement suspend() and
|
people write bus drivers; most driver code is a "device driver" that
|
||||||
resume() methods in struct pci_driver. The PCI core is simply
|
builds on top of bus-specific framework code.
|
||||||
responsible for translating the pointers to PCI-specific ones and
|
|
||||||
calling the low-level driver.
|
|
||||||
|
|
||||||
This is done to a) ease transition to the new power management methods
|
For more information on these driver calls, see the description later;
|
||||||
and leverage the existing PM code in various bus drivers; b) allow
|
they are called in phases for every device, respecting the parent-child
|
||||||
buses to implement generic and default PM routines for devices, and c)
|
sequencing in the driver model tree. Note that as this is being written,
|
||||||
make the flow of execution obvious to the reader.
|
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
|
The "can_wakeup" flag just records whether the device (and its driver) can
|
||||||
a depth-first fashion to transition each device into a low-power
|
physically support wakeup events. When that flag is clear, the sysfs
|
||||||
state. The ordering of the device tree is guaranteed by the order in
|
"wakeup" file is empty, and device_may_wakeup() returns false.
|
||||||
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.
|
|
||||||
|
|
||||||
Devices are suspended once with interrupts enabled. Drivers are
|
For devices that can issue wakeup events, a separate flag controls whether
|
||||||
expected to stop I/O transactions, save device state, and place the
|
that device should try to use its wakeup mechanism. The initial value of
|
||||||
device into a low-power state. Drivers may sleep, allocate memory,
|
device_may_wakeup() will be true, so that the device's "wakeup" file holds
|
||||||
etc. at will.
|
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
|
EXAMPLE: PCI Device Driver Methods
|
||||||
themselves back on with interrupts disabled. Once interrupts have been
|
-----------------------------------
|
||||||
re-enabled, the rest of the drivers will be called to resume their
|
PCI framework software calls these methods when the PCI device driver bound
|
||||||
devices. On resume, a driver is responsible for powering back on each
|
to a device device has provided them:
|
||||||
device, restoring state, and re-enabling I/O transactions for that
|
|
||||||
device.
|
|
||||||
|
|
||||||
|
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
|
System devices follow a slightly different API, which can be found in
|
||||||
|
|
||||||
include/linux/sysdev.h
|
include/linux/sysdev.h
|
||||||
drivers/base/sys.c
|
drivers/base/sys.c
|
||||||
|
|
||||||
System devices will only be suspended with interrupts disabled, and
|
System devices will only be suspended with interrupts disabled, and after
|
||||||
after all other devices have been suspended. On resume, they will be
|
all other devices have been suspended. On resume, they will be resumed
|
||||||
resumed before any other devices, and also with interrupts disabled.
|
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
|
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
|
However, note that if a driver puts a device into a runtime low power state
|
||||||
still running. This feature is useful for devices that are not being
|
and the system then goes into a system-wide sleep state, it normally ought
|
||||||
used, and can offer significant power savings on a running system.
|
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
|
Power Saving Techniques
|
||||||
that the device belongs to if the specified state is not 0, or
|
-----------------------
|
||||||
->resume() if it is.
|
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
|
Using information provided by other system layers
|
||||||
device is currently in.
|
- 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
|
Using fewer CPU cycles
|
||||||
is another, but different, low-power state, the ->resume() method will
|
- using DMA instead of PIO
|
||||||
first be called to power the device back on, then ->suspend() will be
|
- removing timers, or making them lower frequency
|
||||||
called again with the new state.
|
- 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
|
Reducing other resource costs
|
||||||
and putting it into the low-power state specified. If this was
|
- gating off unused clocks in software (or hardware)
|
||||||
successful, it returns 0, and the device's power_state field is
|
- switching off unused power supplies
|
||||||
updated.
|
- 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
|
Using device-specific low power states
|
||||||
properly resume the device, including all step of reinitialization
|
- using lower voltages
|
||||||
necessary. (This is the hardest part, and the one most protected by
|
- avoiding needless DMA transfers
|
||||||
NDA'd documents).
|
|
||||||
|
|
||||||
The driver must also take care not to suspend a device that is
|
Read your hardware documentation carefully to see the opportunities that
|
||||||
currently in use. It is their responsibility to provide their own
|
may be available. If you can, measure the actual power usage and check
|
||||||
exclusion mechanisms.
|
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
|
Examples: USB hosts, system timer, system CPU
|
||||||
supports a priori. This will change in the future.
|
----------------------------------------------
|
||||||
|
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
|
System devices like timers and CPUs may have special roles in the platform
|
||||||
does not know event code, it aborts the request, returning error. Some
|
power management scheme. For example, system timers using a "dynamic tick"
|
||||||
drivers may need to deal with special cases based on the actual type
|
approach don't just save CPU cycles (by eliminating needless timer IRQs),
|
||||||
of suspend operation being done at the system level. This is why
|
but they may also open the door to using lower power CPU "idle" states that
|
||||||
there are flags.
|
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
|
If the CPU can have a "cpufreq" driver, there also may be opportunities
|
||||||
HW.
|
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
|
/sys/devices/.../power/state files
|
||||||
scratch. That probably means stop accepting upstream requests, the
|
==================================
|
||||||
actual policy of what to do with them being specific to a given
|
For now you can also test some of this functionality using sysfs.
|
||||||
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.
|
|
||||||
|
|
||||||
SUSPEND -- like FREEZE, but also put hardware into low-power state. If
|
DEPRECATED: USE "power/state" ONLY FOR DRIVER TESTING, AND
|
||||||
there's need to distinguish several levels of sleep, additional flag
|
AVOID USING dev->power.power_state IN DRIVERS.
|
||||||
is probably best way to do that.
|
|
||||||
|
|
||||||
Transitions are only from a resumed state to a suspended state, never
|
THESE WILL BE REMOVED. IF THE "power/state" FILE GETS REPLACED,
|
||||||
between 2 suspended states. (ON -> FREEZE or ON -> SUSPEND can happen,
|
IT WILL BECOME SOMETHING COUPLED TO THE BUS OR DRIVER.
|
||||||
FREEZE -> SUSPEND or SUSPEND -> FREEZE can not).
|
|
||||||
|
|
||||||
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
|
* Reading from this file displays a value corresponding to
|
||||||
should only look at event, and ignore flags.]
|
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
|
* Writing to this file initiates a transition using the
|
||||||
#enter suspend state. This gives drivers chance to load firmware from
|
specified event code number; only '0', '2', and '3' are
|
||||||
#disk and store it in memory, or do other activities taht require
|
accepted (without a newline); '2' and '3' are both
|
||||||
#operating userland, ability to kmalloc GFP_KERNEL, etc... All of these
|
mapped to PM_EVENT_SUSPEND.
|
||||||
#are forbiden once the suspend dance is started.. event = ON, flags =
|
|
||||||
#PREPARE_TO_SUSPEND
|
|
||||||
|
|
||||||
Apm standby -- prepare for APM event. Quiesce devices to make life
|
On writes, the PM core relies on that recorded event code and the device/bus
|
||||||
easier for APM BIOS. event = FREEZE, flags = APM_STANDBY
|
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
|
- If the bus requires the irqs-disabled suspend_late()/resume_early()
|
||||||
spinning down disks. event = FREEZE, flags = APM_SUSPEND
|
phases, writes fail because those operations are not supported here.
|
||||||
|
|
||||||
System halt, reboot -- quiesce devices to make life easier for BIOS. event
|
- If the recorded value is the expected value, nothing is done.
|
||||||
= FREEZE, flags = SYSTEM_HALT or SYSTEM_REBOOT
|
|
||||||
|
|
||||||
System shutdown -- at least disks need to be spun down, or data may be
|
- If the recorded value is nonzero, the device is partially resumed,
|
||||||
lost. Quiesce devices, just to make life easier for BIOS. event =
|
using the bus.resume() and/or class.resume() methods.
|
||||||
FREEZE, flags = SYSTEM_SHUTDOWN
|
|
||||||
|
|
||||||
Kexec -- turn off DMAs and put hardware into some state where new
|
- If the target value is nonzero, the device is partially suspended,
|
||||||
kernel can take over. event = FREEZE, flags = KEXEC
|
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
|
Drivers have no way to tell whether their suspend() and resume() calls
|
||||||
may need to be enabled on some devices. This actually has at least 3
|
have come through the sysfs power/state file or as part of entering a
|
||||||
subtypes, system can reboot, enter S4 and enter S5 at the end of
|
system sleep state, except that when accessed through sysfs the normal
|
||||||
swsusp. event = FREEZE, flags = SWSUSP and one of SYSTEM_REBOOT,
|
parent/child sequencing rules are ignored. Drivers (such as bus, bridge,
|
||||||
SYSTEM_SHUTDOWN, SYSTEM_S4
|
or hub drivers) which expose child devices may need to enforce those rules
|
||||||
|
on their own.
|
||||||
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
|
|
||||||
#
|
|
||||||
|
@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
|
|||||||
extern int attribute_container_init(void);
|
extern int attribute_container_init(void);
|
||||||
|
|
||||||
extern int bus_add_device(struct device * dev);
|
extern int bus_add_device(struct device * dev);
|
||||||
extern void bus_attach_device(struct device * dev);
|
extern int bus_attach_device(struct device * dev);
|
||||||
extern void bus_remove_device(struct device * dev);
|
extern void bus_remove_device(struct device * dev);
|
||||||
extern struct bus_type *get_bus(struct bus_type * bus);
|
extern struct bus_type *get_bus(struct bus_type * bus);
|
||||||
extern void put_bus(struct bus_type * bus);
|
extern void put_bus(struct bus_type * bus);
|
||||||
|
@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
|
|||||||
if (bus) {
|
if (bus) {
|
||||||
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
|
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
|
||||||
error = device_add_attrs(bus, dev);
|
error = device_add_attrs(bus, dev);
|
||||||
if (!error) {
|
if (error)
|
||||||
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
|
goto out;
|
||||||
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
|
error = sysfs_create_link(&bus->devices.kobj,
|
||||||
sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
|
&dev->kobj, dev->bus_id);
|
||||||
}
|
if (error)
|
||||||
|
goto out;
|
||||||
|
error = sysfs_create_link(&dev->kobj,
|
||||||
|
&dev->bus->subsys.kset.kobj, "subsystem");
|
||||||
|
if (error)
|
||||||
|
goto out;
|
||||||
|
error = sysfs_create_link(&dev->kobj,
|
||||||
|
&dev->bus->subsys.kset.kobj, "bus");
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
|
|||||||
* bus_attach_device - add device to bus
|
* bus_attach_device - add device to bus
|
||||||
* @dev: device tried to attach to a driver
|
* @dev: device tried to attach to a driver
|
||||||
*
|
*
|
||||||
|
* - Add device to bus's list of devices.
|
||||||
* - Try to attach to driver.
|
* - Try to attach to driver.
|
||||||
*/
|
*/
|
||||||
void bus_attach_device(struct device * dev)
|
int bus_attach_device(struct device * dev)
|
||||||
{
|
{
|
||||||
struct bus_type * bus = dev->bus;
|
struct bus_type *bus = dev->bus;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (bus) {
|
if (bus) {
|
||||||
device_attach(dev);
|
dev->is_registered = 1;
|
||||||
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
|
ret = device_attach(dev);
|
||||||
|
if (ret >= 0) {
|
||||||
|
klist_add_tail(&dev->knode_bus, &bus->klist_devices);
|
||||||
|
ret = 0;
|
||||||
|
} else
|
||||||
|
dev->is_registered = 0;
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev)
|
|||||||
sysfs_remove_link(&dev->kobj, "bus");
|
sysfs_remove_link(&dev->kobj, "bus");
|
||||||
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
|
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
|
||||||
device_remove_attrs(dev->bus, dev);
|
device_remove_attrs(dev->bus, dev);
|
||||||
klist_remove(&dev->knode_bus);
|
dev->is_registered = 0;
|
||||||
|
klist_del(&dev->knode_bus);
|
||||||
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
|
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
|
||||||
device_release_driver(dev);
|
device_release_driver(dev);
|
||||||
put_bus(dev->bus);
|
put_bus(dev->bus);
|
||||||
@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
|
|||||||
* Thanks to drivers making their tables __devinit, we can't allow manual
|
* Thanks to drivers making their tables __devinit, we can't allow manual
|
||||||
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
|
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
|
||||||
*/
|
*/
|
||||||
static void add_bind_files(struct device_driver *drv)
|
static int __must_check add_bind_files(struct device_driver *drv)
|
||||||
{
|
{
|
||||||
driver_create_file(drv, &driver_attr_unbind);
|
int ret;
|
||||||
driver_create_file(drv, &driver_attr_bind);
|
|
||||||
|
ret = driver_create_file(drv, &driver_attr_unbind);
|
||||||
|
if (ret == 0) {
|
||||||
|
ret = driver_create_file(drv, &driver_attr_bind);
|
||||||
|
if (ret)
|
||||||
|
driver_remove_file(drv, &driver_attr_unbind);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_bind_files(struct device_driver *drv)
|
static void remove_bind_files(struct device_driver *drv)
|
||||||
@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
|
|||||||
driver_remove_file(drv, &driver_attr_unbind);
|
driver_remove_file(drv, &driver_attr_unbind);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline void add_bind_files(struct device_driver *drv) {}
|
static inline int add_bind_files(struct device_driver *drv) { return 0; }
|
||||||
static inline void remove_bind_files(struct device_driver *drv) {}
|
static inline void remove_bind_files(struct device_driver *drv) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
|
|||||||
* @drv: driver.
|
* @drv: driver.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int bus_add_driver(struct device_driver * drv)
|
int bus_add_driver(struct device_driver *drv)
|
||||||
{
|
{
|
||||||
struct bus_type * bus = get_bus(drv->bus);
|
struct bus_type * bus = get_bus(drv->bus);
|
||||||
int error = 0;
|
int error = 0;
|
||||||
@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
|
|||||||
if (bus) {
|
if (bus) {
|
||||||
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
|
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
|
||||||
error = kobject_set_name(&drv->kobj, "%s", drv->name);
|
error = kobject_set_name(&drv->kobj, "%s", drv->name);
|
||||||
if (error) {
|
if (error)
|
||||||
put_bus(bus);
|
goto out_put_bus;
|
||||||
return error;
|
|
||||||
}
|
|
||||||
drv->kobj.kset = &bus->drivers;
|
drv->kobj.kset = &bus->drivers;
|
||||||
if ((error = kobject_register(&drv->kobj))) {
|
if ((error = kobject_register(&drv->kobj)))
|
||||||
put_bus(bus);
|
goto out_put_bus;
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
driver_attach(drv);
|
error = driver_attach(drv);
|
||||||
|
if (error)
|
||||||
|
goto out_unregister;
|
||||||
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
|
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
|
||||||
module_add_driver(drv->owner, drv);
|
module_add_driver(drv->owner, drv);
|
||||||
|
|
||||||
driver_add_attrs(bus, drv);
|
error = driver_add_attrs(bus, drv);
|
||||||
add_bind_files(drv);
|
if (error) {
|
||||||
|
/* How the hell do we get out of this pickle? Give up */
|
||||||
|
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
|
||||||
|
__FUNCTION__, drv->name);
|
||||||
|
}
|
||||||
|
error = add_bind_files(drv);
|
||||||
|
if (error) {
|
||||||
|
/* Ditto */
|
||||||
|
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
|
||||||
|
__FUNCTION__, drv->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
|
out_unregister:
|
||||||
|
kobject_unregister(&drv->kobj);
|
||||||
|
out_put_bus:
|
||||||
|
put_bus(bus);
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bus_remove_driver - delete driver from bus's knowledge.
|
* bus_remove_driver - delete driver from bus's knowledge.
|
||||||
* @drv: driver.
|
* @drv: driver.
|
||||||
@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
|
|||||||
|
|
||||||
|
|
||||||
/* Helper for bus_rescan_devices's iter */
|
/* Helper for bus_rescan_devices's iter */
|
||||||
static int bus_rescan_devices_helper(struct device *dev, void *data)
|
static int __must_check bus_rescan_devices_helper(struct device *dev,
|
||||||
|
void *data)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!dev->driver) {
|
if (!dev->driver) {
|
||||||
if (dev->parent) /* Needed for USB */
|
if (dev->parent) /* Needed for USB */
|
||||||
down(&dev->parent->sem);
|
down(&dev->parent->sem);
|
||||||
device_attach(dev);
|
ret = device_attach(dev);
|
||||||
if (dev->parent)
|
if (dev->parent)
|
||||||
up(&dev->parent->sem);
|
up(&dev->parent->sem);
|
||||||
|
if (ret > 0)
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return ret < 0 ? ret : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data)
|
|||||||
* attached and rescan it against existing drivers to see if it matches
|
* attached and rescan it against existing drivers to see if it matches
|
||||||
* any by calling device_attach() for the unbound devices.
|
* any by calling device_attach() for the unbound devices.
|
||||||
*/
|
*/
|
||||||
void bus_rescan_devices(struct bus_type * bus)
|
int bus_rescan_devices(struct bus_type * bus)
|
||||||
{
|
{
|
||||||
bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
|
return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus)
|
|||||||
* to use if probing criteria changed during a devices lifetime and
|
* to use if probing criteria changed during a devices lifetime and
|
||||||
* driver attachment should change accordingly.
|
* driver attachment should change accordingly.
|
||||||
*/
|
*/
|
||||||
void device_reprobe(struct device *dev)
|
int device_reprobe(struct device *dev)
|
||||||
{
|
{
|
||||||
if (dev->driver) {
|
if (dev->driver) {
|
||||||
if (dev->parent) /* Needed for USB */
|
if (dev->parent) /* Needed for USB */
|
||||||
@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
|
|||||||
if (dev->parent)
|
if (dev->parent)
|
||||||
up(&dev->parent->sem);
|
up(&dev->parent->sem);
|
||||||
}
|
}
|
||||||
|
return bus_rescan_devices_helper(dev, NULL);
|
||||||
bus_rescan_devices_helper(dev, NULL);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_reprobe);
|
EXPORT_SYMBOL_GPL(device_reprobe);
|
||||||
|
|
||||||
struct bus_type * get_bus(struct bus_type * bus)
|
struct bus_type *get_bus(struct bus_type *bus)
|
||||||
{
|
{
|
||||||
return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL;
|
return bus ? container_of(subsys_get(&bus->subsys),
|
||||||
|
struct bus_type, subsys) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void put_bus(struct bus_type * bus)
|
void put_bus(struct bus_type * bus)
|
||||||
@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
|
|||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void klist_drivers_get(struct klist_node *n)
|
|
||||||
{
|
|
||||||
struct device_driver *drv = container_of(n, struct device_driver,
|
|
||||||
knode_bus);
|
|
||||||
|
|
||||||
get_driver(drv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void klist_drivers_put(struct klist_node *n)
|
|
||||||
{
|
|
||||||
struct device_driver *drv = container_of(n, struct device_driver,
|
|
||||||
knode_bus);
|
|
||||||
|
|
||||||
put_driver(drv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bus_register - register a bus with the system.
|
* bus_register - register a bus with the system.
|
||||||
* @bus: bus.
|
* @bus: bus.
|
||||||
@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
|
|||||||
goto bus_drivers_fail;
|
goto bus_drivers_fail;
|
||||||
|
|
||||||
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
|
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
|
||||||
klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put);
|
klist_init(&bus->klist_drivers, NULL, NULL);
|
||||||
bus_add_attrs(bus);
|
bus_add_attrs(bus);
|
||||||
|
|
||||||
pr_debug("bus type '%s' registered\n", bus->name);
|
pr_debug("bus type '%s' registered\n", bus->name);
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
|
|
||||||
|
extern struct subsystem devices_subsys;
|
||||||
|
|
||||||
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
|
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
|
||||||
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
|
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
|
||||||
|
|
||||||
@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
|
|||||||
* Note, the pointer created here is to be destroyed when finished by
|
* Note, the pointer created here is to be destroyed when finished by
|
||||||
* making a call to class_destroy().
|
* making a call to class_destroy().
|
||||||
*/
|
*/
|
||||||
struct class *class_create(struct module *owner, char *name)
|
struct class *class_create(struct module *owner, const char *name)
|
||||||
{
|
{
|
||||||
struct class *cls;
|
struct class *cls;
|
||||||
int retval;
|
int retval;
|
||||||
@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
|
|||||||
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
|
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
|
||||||
|
|
||||||
if (class_dev->dev) {
|
if (class_dev->dev) {
|
||||||
/* add physical device, backing this device */
|
/* add device, backing this class device (deprecated) */
|
||||||
struct device *dev = class_dev->dev;
|
struct device *dev = class_dev->dev;
|
||||||
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
|
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
|
||||||
|
|
||||||
@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev)
|
|||||||
struct class_device *class_device_create(struct class *cls,
|
struct class_device *class_device_create(struct class *cls,
|
||||||
struct class_device *parent,
|
struct class_device *parent,
|
||||||
dev_t devt,
|
dev_t devt,
|
||||||
struct device *device, char *fmt, ...)
|
struct device *device,
|
||||||
|
const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
struct class_device *class_dev = NULL;
|
struct class_device *class_dev = NULL;
|
||||||
@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
|
|||||||
{
|
{
|
||||||
struct class *parent;
|
struct class *parent;
|
||||||
struct class_device *class_dev;
|
struct class_device *class_dev;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
if (!class_intf || !class_intf->class)
|
if (!class_intf || !class_intf->class)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
|
|||||||
list_for_each_entry(class_dev, &parent->children, node)
|
list_for_each_entry(class_dev, &parent->children, node)
|
||||||
class_intf->add(class_dev, class_intf);
|
class_intf->add(class_dev, class_intf);
|
||||||
}
|
}
|
||||||
|
if (class_intf->add_dev) {
|
||||||
|
list_for_each_entry(dev, &parent->devices, node)
|
||||||
|
class_intf->add_dev(dev, class_intf);
|
||||||
|
}
|
||||||
up(&parent->sem);
|
up(&parent->sem);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
|
|||||||
{
|
{
|
||||||
struct class * parent = class_intf->class;
|
struct class * parent = class_intf->class;
|
||||||
struct class_device *class_dev;
|
struct class_device *class_dev;
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return;
|
return;
|
||||||
@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
|
|||||||
list_for_each_entry(class_dev, &parent->children, node)
|
list_for_each_entry(class_dev, &parent->children, node)
|
||||||
class_intf->remove(class_dev, class_intf);
|
class_intf->remove(class_dev, class_intf);
|
||||||
}
|
}
|
||||||
|
if (class_intf->remove_dev) {
|
||||||
|
list_for_each_entry(dev, &parent->devices, node)
|
||||||
|
class_intf->remove_dev(dev, class_intf);
|
||||||
|
}
|
||||||
up(&parent->sem);
|
up(&parent->sem);
|
||||||
|
|
||||||
class_put(parent);
|
class_put(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int virtual_device_parent(struct device *dev)
|
||||||
|
{
|
||||||
|
if (!dev->class)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (!dev->class->virtual_dir) {
|
||||||
|
static struct kobject *virtual_dir = NULL;
|
||||||
|
|
||||||
|
if (!virtual_dir)
|
||||||
|
virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
|
||||||
|
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->kobj.parent = dev->class->virtual_dir;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int __init classes_init(void)
|
int __init classes_init(void)
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2002-3 Patrick Mochel
|
* Copyright (c) 2002-3 Patrick Mochel
|
||||||
* Copyright (c) 2002-3 Open Source Development Labs
|
* Copyright (c) 2002-3 Open Source Development Labs
|
||||||
|
* Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
|
||||||
|
* Copyright (c) 2006 Novell, Inc.
|
||||||
*
|
*
|
||||||
* This file is released under the GPLv2
|
* This file is released under the GPLv2
|
||||||
*
|
*
|
||||||
@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj)
|
|||||||
|
|
||||||
if (dev->release)
|
if (dev->release)
|
||||||
dev->release(dev);
|
dev->release(dev);
|
||||||
|
else if (dev->class && dev->class->dev_release)
|
||||||
|
dev->class->dev_release(dev);
|
||||||
else {
|
else {
|
||||||
printk(KERN_ERR "Device '%s' does not have a release() function, "
|
printk(KERN_ERR "Device '%s' does not have a release() function, "
|
||||||
"it is broken and must be fixed.\n",
|
"it is broken and must be fixed.\n",
|
||||||
@ -149,17 +153,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
|
|||||||
"MINOR=%u", MINOR(dev->devt));
|
"MINOR=%u", MINOR(dev->devt));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add bus name of physical device */
|
/* add bus name (same as SUBSYSTEM, deprecated) */
|
||||||
if (dev->bus)
|
if (dev->bus)
|
||||||
add_uevent_var(envp, num_envp, &i,
|
add_uevent_var(envp, num_envp, &i,
|
||||||
buffer, buffer_size, &length,
|
buffer, buffer_size, &length,
|
||||||
"PHYSDEVBUS=%s", dev->bus->name);
|
"PHYSDEVBUS=%s", dev->bus->name);
|
||||||
|
|
||||||
/* add driver name of physical device */
|
/* add driver name (PHYSDEV* values are deprecated)*/
|
||||||
if (dev->driver)
|
if (dev->driver) {
|
||||||
|
add_uevent_var(envp, num_envp, &i,
|
||||||
|
buffer, buffer_size, &length,
|
||||||
|
"DRIVER=%s", dev->driver->name);
|
||||||
add_uevent_var(envp, num_envp, &i,
|
add_uevent_var(envp, num_envp, &i,
|
||||||
buffer, buffer_size, &length,
|
buffer, buffer_size, &length,
|
||||||
"PHYSDEVDRIVER=%s", dev->driver->name);
|
"PHYSDEVDRIVER=%s", dev->driver->name);
|
||||||
|
}
|
||||||
|
|
||||||
/* terminate, set to next free slot, shrink available space */
|
/* terminate, set to next free slot, shrink available space */
|
||||||
envp[i] = NULL;
|
envp[i] = NULL;
|
||||||
@ -177,6 +185,15 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dev->class && dev->class->dev_uevent) {
|
||||||
|
/* have the class specific function add its stuff */
|
||||||
|
retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
|
||||||
|
if (retval) {
|
||||||
|
pr_debug("%s - dev_uevent() returned %d\n",
|
||||||
|
__FUNCTION__, retval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int device_add_groups(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if (dev->groups) {
|
||||||
|
for (i = 0; dev->groups[i]; i++) {
|
||||||
|
error = sysfs_create_group(&dev->kobj, dev->groups[i]);
|
||||||
|
if (error) {
|
||||||
|
while (--i >= 0)
|
||||||
|
sysfs_remove_group(&dev->kobj, dev->groups[i]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device_remove_groups(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (dev->groups) {
|
||||||
|
for (i = 0; dev->groups[i]; i++) {
|
||||||
|
sysfs_remove_group(&dev->kobj, dev->groups[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_add_attrs(struct device *dev)
|
||||||
|
{
|
||||||
|
struct class *class = dev->class;
|
||||||
|
int error = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!class)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (class->dev_attrs) {
|
||||||
|
for (i = 0; attr_name(class->dev_attrs[i]); i++) {
|
||||||
|
error = device_create_file(dev, &class->dev_attrs[i]);
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
while (--i >= 0)
|
||||||
|
device_remove_file(dev, &class->dev_attrs[i]);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device_remove_attrs(struct device *dev)
|
||||||
|
{
|
||||||
|
struct class *class = dev->class;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!class)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (class->dev_attrs) {
|
||||||
|
for (i = 0; attr_name(class->dev_attrs[i]); i++)
|
||||||
|
device_remove_file(dev, &class->dev_attrs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
|
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
@ -236,6 +319,32 @@ void device_remove_file(struct device * dev, struct device_attribute * attr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* device_create_bin_file - create sysfs binary attribute file for device.
|
||||||
|
* @dev: device.
|
||||||
|
* @attr: device binary attribute descriptor.
|
||||||
|
*/
|
||||||
|
int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
|
||||||
|
{
|
||||||
|
int error = -EINVAL;
|
||||||
|
if (dev)
|
||||||
|
error = sysfs_create_bin_file(&dev->kobj, attr);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(device_create_bin_file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* device_remove_bin_file - remove sysfs binary attribute file
|
||||||
|
* @dev: device.
|
||||||
|
* @attr: device binary attribute descriptor.
|
||||||
|
*/
|
||||||
|
void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
|
||||||
|
{
|
||||||
|
if (dev)
|
||||||
|
sysfs_remove_bin_file(&dev->kobj, attr);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(device_remove_bin_file);
|
||||||
|
|
||||||
static void klist_children_get(struct klist_node *n)
|
static void klist_children_get(struct klist_node *n)
|
||||||
{
|
{
|
||||||
struct device *dev = container_of(n, struct device, knode_parent);
|
struct device *dev = container_of(n, struct device, knode_parent);
|
||||||
@ -289,12 +398,20 @@ int device_add(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct device *parent = NULL;
|
struct device *parent = NULL;
|
||||||
char *class_name = NULL;
|
char *class_name = NULL;
|
||||||
|
struct class_interface *class_intf;
|
||||||
int error = -EINVAL;
|
int error = -EINVAL;
|
||||||
|
|
||||||
dev = get_device(dev);
|
dev = get_device(dev);
|
||||||
if (!dev || !strlen(dev->bus_id))
|
if (!dev || !strlen(dev->bus_id))
|
||||||
goto Error;
|
goto Error;
|
||||||
|
|
||||||
|
/* if this is a class device, and has no parent, create one */
|
||||||
|
if ((dev->class) && (dev->parent == NULL)) {
|
||||||
|
error = virtual_device_parent(dev);
|
||||||
|
if (error)
|
||||||
|
goto Error;
|
||||||
|
}
|
||||||
|
|
||||||
parent = get_device(dev->parent);
|
parent = get_device(dev->parent);
|
||||||
|
|
||||||
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
|
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
|
||||||
@ -307,6 +424,10 @@ int device_add(struct device *dev)
|
|||||||
if ((error = kobject_add(&dev->kobj)))
|
if ((error = kobject_add(&dev->kobj)))
|
||||||
goto Error;
|
goto Error;
|
||||||
|
|
||||||
|
/* notify platform of device entry */
|
||||||
|
if (platform_notify)
|
||||||
|
platform_notify(dev);
|
||||||
|
|
||||||
dev->uevent_attr.attr.name = "uevent";
|
dev->uevent_attr.attr.name = "uevent";
|
||||||
dev->uevent_attr.attr.mode = S_IWUSR;
|
dev->uevent_attr.attr.mode = S_IWUSR;
|
||||||
if (dev->driver)
|
if (dev->driver)
|
||||||
@ -340,12 +461,17 @@ int device_add(struct device *dev)
|
|||||||
"subsystem");
|
"subsystem");
|
||||||
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
|
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
|
||||||
dev->bus_id);
|
dev->bus_id);
|
||||||
|
if (parent) {
|
||||||
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
|
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
|
||||||
class_name = make_class_name(dev->class->name, &dev->kobj);
|
class_name = make_class_name(dev->class->name, &dev->kobj);
|
||||||
sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
|
sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((error = device_add_attrs(dev)))
|
||||||
|
goto AttrsError;
|
||||||
|
if ((error = device_add_groups(dev)))
|
||||||
|
goto GroupError;
|
||||||
if ((error = device_pm_add(dev)))
|
if ((error = device_pm_add(dev)))
|
||||||
goto PMError;
|
goto PMError;
|
||||||
if ((error = bus_add_device(dev)))
|
if ((error = bus_add_device(dev)))
|
||||||
@ -356,15 +482,16 @@ int device_add(struct device *dev)
|
|||||||
klist_add_tail(&dev->knode_parent, &parent->klist_children);
|
klist_add_tail(&dev->knode_parent, &parent->klist_children);
|
||||||
|
|
||||||
if (dev->class) {
|
if (dev->class) {
|
||||||
/* tie the class to the device */
|
|
||||||
down(&dev->class->sem);
|
down(&dev->class->sem);
|
||||||
|
/* tie the class to the device */
|
||||||
list_add_tail(&dev->node, &dev->class->devices);
|
list_add_tail(&dev->node, &dev->class->devices);
|
||||||
|
|
||||||
|
/* notify any interfaces that the device is here */
|
||||||
|
list_for_each_entry(class_intf, &dev->class->interfaces, node)
|
||||||
|
if (class_intf->add_dev)
|
||||||
|
class_intf->add_dev(dev, class_intf);
|
||||||
up(&dev->class->sem);
|
up(&dev->class->sem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* notify platform of device entry */
|
|
||||||
if (platform_notify)
|
|
||||||
platform_notify(dev);
|
|
||||||
Done:
|
Done:
|
||||||
kfree(class_name);
|
kfree(class_name);
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
@ -372,6 +499,10 @@ int device_add(struct device *dev)
|
|||||||
BusError:
|
BusError:
|
||||||
device_pm_remove(dev);
|
device_pm_remove(dev);
|
||||||
PMError:
|
PMError:
|
||||||
|
device_remove_groups(dev);
|
||||||
|
GroupError:
|
||||||
|
device_remove_attrs(dev);
|
||||||
|
AttrsError:
|
||||||
if (dev->devt_attr) {
|
if (dev->devt_attr) {
|
||||||
device_remove_file(dev, dev->devt_attr);
|
device_remove_file(dev, dev->devt_attr);
|
||||||
kfree(dev->devt_attr);
|
kfree(dev->devt_attr);
|
||||||
@ -449,6 +580,7 @@ void device_del(struct device * dev)
|
|||||||
{
|
{
|
||||||
struct device * parent = dev->parent;
|
struct device * parent = dev->parent;
|
||||||
char *class_name = NULL;
|
char *class_name = NULL;
|
||||||
|
struct class_interface *class_intf;
|
||||||
|
|
||||||
if (parent)
|
if (parent)
|
||||||
klist_del(&dev->knode_parent);
|
klist_del(&dev->knode_parent);
|
||||||
@ -458,14 +590,23 @@ void device_del(struct device * dev)
|
|||||||
sysfs_remove_link(&dev->kobj, "subsystem");
|
sysfs_remove_link(&dev->kobj, "subsystem");
|
||||||
sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
|
sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
|
||||||
class_name = make_class_name(dev->class->name, &dev->kobj);
|
class_name = make_class_name(dev->class->name, &dev->kobj);
|
||||||
sysfs_remove_link(&dev->kobj, "device");
|
if (parent) {
|
||||||
sysfs_remove_link(&dev->parent->kobj, class_name);
|
sysfs_remove_link(&dev->kobj, "device");
|
||||||
|
sysfs_remove_link(&dev->parent->kobj, class_name);
|
||||||
|
}
|
||||||
kfree(class_name);
|
kfree(class_name);
|
||||||
down(&dev->class->sem);
|
down(&dev->class->sem);
|
||||||
|
/* notify any interfaces that the device is now gone */
|
||||||
|
list_for_each_entry(class_intf, &dev->class->interfaces, node)
|
||||||
|
if (class_intf->remove_dev)
|
||||||
|
class_intf->remove_dev(dev, class_intf);
|
||||||
|
/* remove the device from the class list */
|
||||||
list_del_init(&dev->node);
|
list_del_init(&dev->node);
|
||||||
up(&dev->class->sem);
|
up(&dev->class->sem);
|
||||||
}
|
}
|
||||||
device_remove_file(dev, &dev->uevent_attr);
|
device_remove_file(dev, &dev->uevent_attr);
|
||||||
|
device_remove_groups(dev);
|
||||||
|
device_remove_attrs(dev);
|
||||||
|
|
||||||
/* Notify the platform of the removal, in case they
|
/* Notify the platform of the removal, in case they
|
||||||
* need to do anything...
|
* need to do anything...
|
||||||
@ -579,7 +720,7 @@ static void device_create_release(struct device *dev)
|
|||||||
* been created with a call to class_create().
|
* been created with a call to class_create().
|
||||||
*/
|
*/
|
||||||
struct device *device_create(struct class *class, struct device *parent,
|
struct device *device_create(struct class *class, struct device *parent,
|
||||||
dev_t devt, char *fmt, ...)
|
dev_t devt, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
struct device *dev = NULL;
|
struct device *dev = NULL;
|
||||||
@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent,
|
|||||||
|
|
||||||
if (class == NULL || IS_ERR(class))
|
if (class == NULL || IS_ERR(class))
|
||||||
goto error;
|
goto error;
|
||||||
if (parent == NULL) {
|
|
||||||
printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt)
|
|||||||
device_unregister(dev);
|
device_unregister(dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_destroy);
|
EXPORT_SYMBOL_GPL(device_destroy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* device_rename - renames a device
|
||||||
|
* @dev: the pointer to the struct device to be renamed
|
||||||
|
* @new_name: the new name of the device
|
||||||
|
*/
|
||||||
|
int device_rename(struct device *dev, char *new_name)
|
||||||
|
{
|
||||||
|
char *old_class_name = NULL;
|
||||||
|
char *new_class_name = NULL;
|
||||||
|
char *old_symlink_name = NULL;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
dev = get_device(dev);
|
||||||
|
if (!dev)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
|
||||||
|
|
||||||
|
if ((dev->class) && (dev->parent))
|
||||||
|
old_class_name = make_class_name(dev->class->name, &dev->kobj);
|
||||||
|
|
||||||
|
if (dev->class) {
|
||||||
|
old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
|
||||||
|
if (!old_symlink_name)
|
||||||
|
return -ENOMEM;
|
||||||
|
strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
|
||||||
|
|
||||||
|
error = kobject_rename(&dev->kobj, new_name);
|
||||||
|
|
||||||
|
if (old_class_name) {
|
||||||
|
new_class_name = make_class_name(dev->class->name, &dev->kobj);
|
||||||
|
if (new_class_name) {
|
||||||
|
sysfs_create_link(&dev->parent->kobj, &dev->kobj,
|
||||||
|
new_class_name);
|
||||||
|
sysfs_remove_link(&dev->parent->kobj, old_class_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dev->class) {
|
||||||
|
sysfs_remove_link(&dev->class->subsys.kset.kobj,
|
||||||
|
old_symlink_name);
|
||||||
|
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
|
||||||
|
dev->bus_id);
|
||||||
|
}
|
||||||
|
put_device(dev);
|
||||||
|
|
||||||
|
kfree(old_class_name);
|
||||||
|
kfree(new_class_name);
|
||||||
|
kfree(old_symlink_name);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "power/power.h"
|
#include "power/power.h"
|
||||||
@ -38,66 +39,73 @@
|
|||||||
*
|
*
|
||||||
* This function must be called with @dev->sem held.
|
* This function must be called with @dev->sem held.
|
||||||
*/
|
*/
|
||||||
void device_bind_driver(struct device * dev)
|
int device_bind_driver(struct device *dev)
|
||||||
{
|
{
|
||||||
if (klist_node_attached(&dev->knode_driver))
|
int ret;
|
||||||
return;
|
|
||||||
|
if (klist_node_attached(&dev->knode_driver)) {
|
||||||
|
printk(KERN_WARNING "%s: device %s already bound\n",
|
||||||
|
__FUNCTION__, kobject_name(&dev->kobj));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pr_debug("bound device '%s' to driver '%s'\n",
|
pr_debug("bound device '%s' to driver '%s'\n",
|
||||||
dev->bus_id, dev->driver->name);
|
dev->bus_id, dev->driver->name);
|
||||||
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
|
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
|
||||||
sysfs_create_link(&dev->driver->kobj, &dev->kobj,
|
ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
|
||||||
kobject_name(&dev->kobj));
|
kobject_name(&dev->kobj));
|
||||||
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
|
if (ret == 0) {
|
||||||
|
ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
|
||||||
|
"driver");
|
||||||
|
if (ret)
|
||||||
|
sysfs_remove_link(&dev->driver->kobj,
|
||||||
|
kobject_name(&dev->kobj));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
struct stupid_thread_structure {
|
||||||
* driver_probe_device - attempt to bind device & driver.
|
struct device_driver *drv;
|
||||||
* @drv: driver.
|
struct device *dev;
|
||||||
* @dev: device.
|
};
|
||||||
*
|
|
||||||
* First, we call the bus's match function, if one present, which
|
static atomic_t probe_count = ATOMIC_INIT(0);
|
||||||
* should compare the device IDs the driver supports with the
|
static int really_probe(void *void_data)
|
||||||
* device IDs of the device. Note we don't do this ourselves
|
|
||||||
* because we don't know the format of the ID structures, nor what
|
|
||||||
* is to be considered a match and what is not.
|
|
||||||
*
|
|
||||||
* This function returns 1 if a match is found, an error if one
|
|
||||||
* occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
|
|
||||||
*
|
|
||||||
* This function must be called with @dev->sem held. When called
|
|
||||||
* for a USB interface, @dev->parent->sem must be held as well.
|
|
||||||
*/
|
|
||||||
int driver_probe_device(struct device_driver * drv, struct device * dev)
|
|
||||||
{
|
{
|
||||||
|
struct stupid_thread_structure *data = void_data;
|
||||||
|
struct device_driver *drv = data->drv;
|
||||||
|
struct device *dev = data->dev;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (drv->bus->match && !drv->bus->match(dev, drv))
|
atomic_inc(&probe_count);
|
||||||
goto Done;
|
pr_debug("%s: Probing driver %s with device %s\n",
|
||||||
|
drv->bus->name, drv->name, dev->bus_id);
|
||||||
|
|
||||||
pr_debug("%s: Matched Device %s with Driver %s\n",
|
|
||||||
drv->bus->name, dev->bus_id, drv->name);
|
|
||||||
dev->driver = drv;
|
dev->driver = drv;
|
||||||
if (dev->bus->probe) {
|
if (dev->bus->probe) {
|
||||||
ret = dev->bus->probe(dev);
|
ret = dev->bus->probe(dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev->driver = NULL;
|
dev->driver = NULL;
|
||||||
goto ProbeFailed;
|
goto probe_failed;
|
||||||
}
|
}
|
||||||
} else if (drv->probe) {
|
} else if (drv->probe) {
|
||||||
ret = drv->probe(dev);
|
ret = drv->probe(dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev->driver = NULL;
|
dev->driver = NULL;
|
||||||
goto ProbeFailed;
|
goto probe_failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device_bind_driver(dev);
|
if (device_bind_driver(dev)) {
|
||||||
|
printk(KERN_ERR "%s: device_bind_driver(%s) failed\n",
|
||||||
|
__FUNCTION__, dev->bus_id);
|
||||||
|
/* How does undo a ->probe? We're screwed. */
|
||||||
|
}
|
||||||
ret = 1;
|
ret = 1;
|
||||||
pr_debug("%s: Bound Device %s to Driver %s\n",
|
pr_debug("%s: Bound Device %s to Driver %s\n",
|
||||||
drv->bus->name, dev->bus_id, drv->name);
|
drv->bus->name, dev->bus_id, drv->name);
|
||||||
goto Done;
|
goto done;
|
||||||
|
|
||||||
ProbeFailed:
|
probe_failed:
|
||||||
if (ret == -ENODEV || ret == -ENXIO) {
|
if (ret == -ENODEV || ret == -ENXIO) {
|
||||||
/* Driver matched, but didn't support device
|
/* Driver matched, but didn't support device
|
||||||
* or device not found.
|
* or device not found.
|
||||||
@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
|
|||||||
"%s: probe of %s failed with error %d\n",
|
"%s: probe of %s failed with error %d\n",
|
||||||
drv->name, dev->bus_id, ret);
|
drv->name, dev->bus_id, ret);
|
||||||
}
|
}
|
||||||
Done:
|
done:
|
||||||
|
kfree(data);
|
||||||
|
atomic_dec(&probe_count);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* driver_probe_done
|
||||||
|
* Determine if the probe sequence is finished or not.
|
||||||
|
*
|
||||||
|
* Should somehow figure out how to use a semaphore, not an atomic variable...
|
||||||
|
*/
|
||||||
|
int driver_probe_done(void)
|
||||||
|
{
|
||||||
|
pr_debug("%s: probe_count = %d\n", __FUNCTION__,
|
||||||
|
atomic_read(&probe_count));
|
||||||
|
if (atomic_read(&probe_count))
|
||||||
|
return -EBUSY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* driver_probe_device - attempt to bind device & driver together
|
||||||
|
* @drv: driver to bind a device to
|
||||||
|
* @dev: device to try to bind to the driver
|
||||||
|
*
|
||||||
|
* First, we call the bus's match function, if one present, which should
|
||||||
|
* compare the device IDs the driver supports with the device IDs of the
|
||||||
|
* device. Note we don't do this ourselves because we don't know the
|
||||||
|
* format of the ID structures, nor what is to be considered a match and
|
||||||
|
* what is not.
|
||||||
|
*
|
||||||
|
* This function returns 1 if a match is found, an error if one occurs
|
||||||
|
* (that is not -ENODEV or -ENXIO), and 0 otherwise.
|
||||||
|
*
|
||||||
|
* This function must be called with @dev->sem held. When called for a
|
||||||
|
* USB interface, @dev->parent->sem must be held as well.
|
||||||
|
*/
|
||||||
|
int driver_probe_device(struct device_driver * drv, struct device * dev)
|
||||||
|
{
|
||||||
|
struct stupid_thread_structure *data;
|
||||||
|
struct task_struct *probe_task;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!device_is_registered(dev))
|
||||||
|
return -ENODEV;
|
||||||
|
if (drv->bus->match && !drv->bus->match(dev, drv))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
pr_debug("%s: Matched Device %s with Driver %s\n",
|
||||||
|
drv->bus->name, dev->bus_id, drv->name);
|
||||||
|
|
||||||
|
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||||
|
data->drv = drv;
|
||||||
|
data->dev = dev;
|
||||||
|
|
||||||
|
if (drv->multithread_probe) {
|
||||||
|
probe_task = kthread_run(really_probe, data,
|
||||||
|
"probe-%s", dev->bus_id);
|
||||||
|
if (IS_ERR(probe_task))
|
||||||
|
ret = PTR_ERR(probe_task);
|
||||||
|
} else
|
||||||
|
ret = really_probe(data);
|
||||||
|
|
||||||
|
done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,8 +211,9 @@ int device_attach(struct device * dev)
|
|||||||
|
|
||||||
down(&dev->sem);
|
down(&dev->sem);
|
||||||
if (dev->driver) {
|
if (dev->driver) {
|
||||||
device_bind_driver(dev);
|
ret = device_bind_driver(dev);
|
||||||
ret = 1;
|
if (ret == 0)
|
||||||
|
ret = 1;
|
||||||
} else
|
} else
|
||||||
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
|
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
|
||||||
up(&dev->sem);
|
up(&dev->sem);
|
||||||
@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data)
|
|||||||
* returns 0 and the @dev->driver is set, we've found a
|
* returns 0 and the @dev->driver is set, we've found a
|
||||||
* compatible pair.
|
* compatible pair.
|
||||||
*/
|
*/
|
||||||
void driver_attach(struct device_driver * drv)
|
int driver_attach(struct device_driver * drv)
|
||||||
{
|
{
|
||||||
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
|
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
|
|||||||
kobject_put(&drv->kobj);
|
kobject_put(&drv->kobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void klist_devices_get(struct klist_node *n)
|
|
||||||
{
|
|
||||||
struct device *dev = container_of(n, struct device, knode_driver);
|
|
||||||
|
|
||||||
get_device(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void klist_devices_put(struct klist_node *n)
|
|
||||||
{
|
|
||||||
struct device *dev = container_of(n, struct device, knode_driver);
|
|
||||||
|
|
||||||
put_device(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* driver_register - register driver with bus
|
* driver_register - register driver with bus
|
||||||
* @drv: driver to register
|
* @drv: driver to register
|
||||||
@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
|
|||||||
(drv->bus->shutdown && drv->shutdown)) {
|
(drv->bus->shutdown && drv->shutdown)) {
|
||||||
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
|
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
|
||||||
}
|
}
|
||||||
klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
|
klist_init(&drv->klist_devices, NULL, NULL);
|
||||||
init_completion(&drv->unloaded);
|
init_completion(&drv->unloaded);
|
||||||
return bus_add_driver(drv);
|
return bus_add_driver(drv);
|
||||||
}
|
}
|
||||||
|
@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv)
|
|||||||
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
|
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int platform_suspend(struct device * dev, pm_message_t state)
|
static int platform_suspend(struct device *dev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (dev->driver && dev->driver->suspend)
|
if (dev->driver && dev->driver->suspend)
|
||||||
ret = dev->driver->suspend(dev, state);
|
ret = dev->driver->suspend(dev, mesg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int platform_suspend_late(struct device *dev, pm_message_t mesg)
|
||||||
|
{
|
||||||
|
struct platform_driver *drv = to_platform_driver(dev->driver);
|
||||||
|
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dev->driver && drv->suspend_late)
|
||||||
|
ret = drv->suspend_late(pdev, mesg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int platform_resume_early(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_driver *drv = to_platform_driver(dev->driver);
|
||||||
|
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (dev->driver && drv->resume_early)
|
||||||
|
ret = drv->resume_early(pdev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
|
|||||||
.match = platform_match,
|
.match = platform_match,
|
||||||
.uevent = platform_uevent,
|
.uevent = platform_uevent,
|
||||||
.suspend = platform_suspend,
|
.suspend = platform_suspend,
|
||||||
|
.suspend_late = platform_suspend_late,
|
||||||
|
.resume_early = platform_resume_early,
|
||||||
.resume = platform_resume,
|
.resume = platform_resume,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_GPL(platform_bus_type);
|
EXPORT_SYMBOL_GPL(platform_bus_type);
|
||||||
|
@ -38,13 +38,35 @@ int resume_device(struct device * dev)
|
|||||||
dev_dbg(dev,"resuming\n");
|
dev_dbg(dev,"resuming\n");
|
||||||
error = dev->bus->resume(dev);
|
error = dev->bus->resume(dev);
|
||||||
}
|
}
|
||||||
|
if (dev->class && dev->class->resume) {
|
||||||
|
dev_dbg(dev,"class resume\n");
|
||||||
|
error = dev->class->resume(dev);
|
||||||
|
}
|
||||||
up(&dev->sem);
|
up(&dev->sem);
|
||||||
TRACE_RESUME(error);
|
TRACE_RESUME(error);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int resume_device_early(struct device * dev)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
TRACE_DEVICE(dev);
|
||||||
|
TRACE_RESUME(0);
|
||||||
|
if (dev->bus && dev->bus->resume_early) {
|
||||||
|
dev_dbg(dev,"EARLY resume\n");
|
||||||
|
error = dev->bus->resume_early(dev);
|
||||||
|
}
|
||||||
|
TRACE_RESUME(error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resume the devices that have either not gone through
|
||||||
|
* the late suspend, or that did go through it but also
|
||||||
|
* went through the early resume
|
||||||
|
*/
|
||||||
void dpm_resume(void)
|
void dpm_resume(void)
|
||||||
{
|
{
|
||||||
down(&dpm_list_sem);
|
down(&dpm_list_sem);
|
||||||
@ -74,6 +96,7 @@ void dpm_resume(void)
|
|||||||
|
|
||||||
void device_resume(void)
|
void device_resume(void)
|
||||||
{
|
{
|
||||||
|
might_sleep();
|
||||||
down(&dpm_sem);
|
down(&dpm_sem);
|
||||||
dpm_resume();
|
dpm_resume();
|
||||||
up(&dpm_sem);
|
up(&dpm_sem);
|
||||||
@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume);
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_power_up_irq - Power on some devices.
|
* dpm_power_up - Power on some devices.
|
||||||
*
|
*
|
||||||
* Walk the dpm_off_irq list and power each device up. This
|
* Walk the dpm_off_irq list and power each device up. This
|
||||||
* is used for devices that required they be powered down with
|
* is used for devices that required they be powered down with
|
||||||
* interrupts disabled. As devices are powered on, they are moved to
|
* interrupts disabled. As devices are powered on, they are moved
|
||||||
* the dpm_suspended list.
|
* to the dpm_active list.
|
||||||
*
|
*
|
||||||
* Interrupts must be disabled when calling this.
|
* Interrupts must be disabled when calling this.
|
||||||
*/
|
*/
|
||||||
@ -99,16 +122,14 @@ void dpm_power_up(void)
|
|||||||
struct list_head * entry = dpm_off_irq.next;
|
struct list_head * entry = dpm_off_irq.next;
|
||||||
struct device * dev = to_device(entry);
|
struct device * dev = to_device(entry);
|
||||||
|
|
||||||
get_device(dev);
|
list_move_tail(entry, &dpm_off);
|
||||||
list_move_tail(entry, &dpm_active);
|
resume_device_early(dev);
|
||||||
resume_device(dev);
|
|
||||||
put_device(dev);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_pm_power_up - Turn on all devices that need special attention.
|
* device_power_up - Turn on all devices that need special attention.
|
||||||
*
|
*
|
||||||
* Power on system devices then devices that required we shut them down
|
* Power on system devices then devices that required we shut them down
|
||||||
* with interrupts disabled.
|
* with interrupts disabled.
|
||||||
|
@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
case PM_EVENT_SUSPEND: return "suspend";
|
case PM_EVENT_SUSPEND: return "suspend";
|
||||||
case PM_EVENT_FREEZE: return "freeze";
|
case PM_EVENT_FREEZE: return "freeze";
|
||||||
|
case PM_EVENT_PRETHAW: return "prethaw";
|
||||||
default: return "(unknown suspend event)";
|
default: return "(unknown suspend event)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|||||||
|
|
||||||
dev->power.prev_state = dev->power.power_state;
|
dev->power.prev_state = dev->power.power_state;
|
||||||
|
|
||||||
if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
|
if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
|
||||||
|
dev_dbg(dev, "class %s%s\n",
|
||||||
|
suspend_verb(state.event),
|
||||||
|
((state.event == PM_EVENT_SUSPEND)
|
||||||
|
&& device_may_wakeup(dev))
|
||||||
|
? ", may wakeup"
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
error = dev->class->suspend(dev, state);
|
||||||
|
suspend_report_result(dev->class->suspend, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
|
||||||
dev_dbg(dev, "%s%s\n",
|
dev_dbg(dev, "%s%s\n",
|
||||||
suspend_verb(state.event),
|
suspend_verb(state.event),
|
||||||
((state.event == PM_EVENT_SUSPEND)
|
((state.event == PM_EVENT_SUSPEND)
|
||||||
@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is called with interrupts off, only a single CPU
|
||||||
|
* running. We can't do down() on a semaphore (and we don't
|
||||||
|
* need the protection)
|
||||||
|
*/
|
||||||
|
static int suspend_device_late(struct device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
|
||||||
|
dev_dbg(dev, "LATE %s%s\n",
|
||||||
|
suspend_verb(state.event),
|
||||||
|
((state.event == PM_EVENT_SUSPEND)
|
||||||
|
&& device_may_wakeup(dev))
|
||||||
|
? ", may wakeup"
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
error = dev->bus->suspend_late(dev, state);
|
||||||
|
suspend_report_result(dev->bus->suspend_late, error);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_suspend - Save state and stop all devices in system.
|
* device_suspend - Save state and stop all devices in system.
|
||||||
* @state: Power state to put each device in.
|
* @state: Power state to put each device in.
|
||||||
*
|
*
|
||||||
* Walk the dpm_active list, call ->suspend() for each device, and move
|
* Walk the dpm_active list, call ->suspend() for each device, and move
|
||||||
* it to dpm_off.
|
* it to the dpm_off list.
|
||||||
* Check the return value for each. If it returns 0, then we move the
|
*
|
||||||
* the device to the dpm_off list. If it returns -EAGAIN, we move it to
|
* (For historical reasons, if it returns -EAGAIN, that used to mean
|
||||||
* the dpm_off_irq list. If we get a different error, try and back out.
|
* that the device would be called again with interrupts disabled.
|
||||||
|
* These days, we use the "suspend_late()" callback for that, so we
|
||||||
|
* print a warning and consider it an error).
|
||||||
|
*
|
||||||
|
* If we get a different error, try and back out.
|
||||||
*
|
*
|
||||||
* If we hit a failure with any of the devices, call device_resume()
|
* If we hit a failure with any of the devices, call device_resume()
|
||||||
* above to bring the suspended devices back to life.
|
* above to bring the suspended devices back to life.
|
||||||
@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
|
|||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
down(&dpm_sem);
|
down(&dpm_sem);
|
||||||
down(&dpm_list_sem);
|
down(&dpm_list_sem);
|
||||||
while (!list_empty(&dpm_active) && error == 0) {
|
while (!list_empty(&dpm_active) && error == 0) {
|
||||||
@ -115,39 +156,27 @@ int device_suspend(pm_message_t state)
|
|||||||
|
|
||||||
/* Check if the device got removed */
|
/* Check if the device got removed */
|
||||||
if (!list_empty(&dev->power.entry)) {
|
if (!list_empty(&dev->power.entry)) {
|
||||||
/* Move it to the dpm_off or dpm_off_irq list */
|
/* Move it to the dpm_off list */
|
||||||
if (!error)
|
if (!error)
|
||||||
list_move(&dev->power.entry, &dpm_off);
|
list_move(&dev->power.entry, &dpm_off);
|
||||||
else if (error == -EAGAIN) {
|
|
||||||
list_move(&dev->power.entry, &dpm_off_irq);
|
|
||||||
error = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (error)
|
if (error)
|
||||||
printk(KERN_ERR "Could not suspend device %s: "
|
printk(KERN_ERR "Could not suspend device %s: "
|
||||||
"error %d\n", kobject_name(&dev->kobj), error);
|
"error %d%s\n",
|
||||||
|
kobject_name(&dev->kobj), error,
|
||||||
|
error == -EAGAIN ? " (please convert to suspend_late)" : "");
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
up(&dpm_list_sem);
|
up(&dpm_list_sem);
|
||||||
if (error) {
|
if (error)
|
||||||
/* we failed... before resuming, bring back devices from
|
|
||||||
* dpm_off_irq list back to main dpm_off list, we do want
|
|
||||||
* to call resume() on them, in case they partially suspended
|
|
||||||
* despite returning -EAGAIN
|
|
||||||
*/
|
|
||||||
while (!list_empty(&dpm_off_irq)) {
|
|
||||||
struct list_head * entry = dpm_off_irq.next;
|
|
||||||
list_move(entry, &dpm_off);
|
|
||||||
}
|
|
||||||
dpm_resume();
|
dpm_resume();
|
||||||
}
|
|
||||||
up(&dpm_sem);
|
up(&dpm_sem);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(device_suspend);
|
EXPORT_SYMBOL_GPL(device_suspend);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_power_down - Shut down special devices.
|
* device_power_down - Shut down special devices.
|
||||||
* @state: Power state to enter.
|
* @state: Power state to enter.
|
||||||
@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
struct device * dev;
|
struct device * dev;
|
||||||
|
|
||||||
list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
|
while (!list_empty(&dpm_off)) {
|
||||||
if ((error = suspend_device(dev, state)))
|
struct list_head * entry = dpm_off.prev;
|
||||||
break;
|
|
||||||
|
dev = to_device(entry);
|
||||||
|
error = suspend_device_late(dev, state);
|
||||||
|
if (error)
|
||||||
|
goto Error;
|
||||||
|
list_move(&dev->power.entry, &dpm_off_irq);
|
||||||
}
|
}
|
||||||
if (error)
|
|
||||||
goto Error;
|
error = sysdev_suspend(state);
|
||||||
if ((error = sysdev_suspend(state)))
|
|
||||||
goto Error;
|
|
||||||
Done:
|
Done:
|
||||||
return error;
|
return error;
|
||||||
Error:
|
Error:
|
||||||
|
@ -7,22 +7,29 @@
|
|||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SYSFS_DEPRECATED
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* state - Control current power state of device
|
* state - Control current power state of device
|
||||||
*
|
*
|
||||||
* show() returns the current power state of the device. '0' indicates
|
* show() returns the current power state of the device. '0' indicates
|
||||||
* the device is on. Other values (1-3) indicate the device is in a low
|
* the device is on. Other values (2) indicate the device is in some low
|
||||||
* power state.
|
* power state.
|
||||||
*
|
*
|
||||||
* store() sets the current power state, which is an integer value
|
* store() sets the current power state, which is an integer valued
|
||||||
* between 0-3. If the device is on ('0'), and the value written is
|
* 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
|
||||||
* greater than 0, then the device is placed directly into the low-power
|
* methods fail this operation; those methods couldn't be called.
|
||||||
* state (via its driver's ->suspend() method).
|
* Otherwise,
|
||||||
* If the device is currently in a low-power state, and the value is 0,
|
*
|
||||||
* the device is powered back on (via the ->resume() method).
|
* - If the recorded dev->power.power_state.event matches the
|
||||||
* If the device is in a low-power state, and a different low-power state
|
* target value, nothing is done.
|
||||||
* is requested, the device is first resumed, then suspended into the new
|
* - If the recorded event code is nonzero, the device is reactivated
|
||||||
* low-power state.
|
* by calling bus.resume() and/or class.resume().
|
||||||
|
* - If the target value is nonzero, the device is suspended by
|
||||||
|
* calling class.suspend() and/or bus.suspend() with event code
|
||||||
|
* PM_EVENT_SUSPEND.
|
||||||
|
*
|
||||||
|
* This mechanism is DEPRECATED and should only be used for testing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
|
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
|
||||||
@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
|
|||||||
pm_message_t state;
|
pm_message_t state;
|
||||||
int error = -EINVAL;
|
int error = -EINVAL;
|
||||||
|
|
||||||
|
/* disallow incomplete suspend sequences */
|
||||||
|
if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
|
||||||
|
return error;
|
||||||
|
|
||||||
state.event = PM_EVENT_SUSPEND;
|
state.event = PM_EVENT_SUSPEND;
|
||||||
/* Older apps expected to write "3" here - confused with PCI D3 */
|
/* Older apps expected to write "3" here - confused with PCI D3 */
|
||||||
if ((n == 1) && !strcmp(buf, "3"))
|
if ((n == 1) && !strcmp(buf, "3"))
|
||||||
@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
|
|||||||
static DEVICE_ATTR(state, 0644, state_show, state_store);
|
static DEVICE_ATTR(state, 0644, state_show, state_store);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM_SYSFS_DEPRECATED */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* wakeup - Report/change current wakeup option for device
|
* wakeup - Report/change current wakeup option for device
|
||||||
*
|
*
|
||||||
@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
|
|||||||
|
|
||||||
|
|
||||||
static struct attribute * power_attrs[] = {
|
static struct attribute * power_attrs[] = {
|
||||||
|
#ifdef CONFIG_PM_SYSFS_DEPRECATED
|
||||||
&dev_attr_state.attr,
|
&dev_attr_state.attr,
|
||||||
|
#endif
|
||||||
&dev_attr_wakeup.attr,
|
&dev_attr_wakeup.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
@ -1207,7 +1207,7 @@ int system_bus_clock (void)
|
|||||||
|
|
||||||
EXPORT_SYMBOL(system_bus_clock);
|
EXPORT_SYMBOL(system_bus_clock);
|
||||||
|
|
||||||
static int generic_ide_suspend(struct device *dev, pm_message_t state)
|
static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
ide_drive_t *drive = dev->driver_data;
|
ide_drive_t *drive = dev->driver_data;
|
||||||
struct request rq;
|
struct request rq;
|
||||||
@ -1221,7 +1221,9 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
|
|||||||
rq.special = &args;
|
rq.special = &args;
|
||||||
rq.end_io_data = &rqpm;
|
rq.end_io_data = &rqpm;
|
||||||
rqpm.pm_step = ide_pm_state_start_suspend;
|
rqpm.pm_step = ide_pm_state_start_suspend;
|
||||||
rqpm.pm_state = state.event;
|
if (mesg.event == PM_EVENT_PRETHAW)
|
||||||
|
mesg.event = PM_EVENT_FREEZE;
|
||||||
|
rqpm.pm_state = mesg.event;
|
||||||
|
|
||||||
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
return ide_do_drive_cmd(drive, &rq, ide_wait);
|
||||||
}
|
}
|
||||||
|
@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t state)
|
pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
|
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >= PM_EVENT_SUSPEND) {
|
if (mesg.event != mdev->ofdev.dev.power.power_state.event
|
||||||
|
&& mesg.event == PM_EVENT_SUSPEND) {
|
||||||
rc = pmac_ide_do_suspend(hwif);
|
rc = pmac_ide_do_suspend(hwif);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
mdev->ofdev.dev.power.power_state = state;
|
mdev->ofdev.dev.power.power_state = mesg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
|
||||||
{
|
{
|
||||||
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
|
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (state.event != pdev->dev.power.power_state.event && state.event >= 2) {
|
if (mesg.event != pdev->dev.power.power_state.event
|
||||||
|
&& mesg.event == PM_EVENT_SUSPEND) {
|
||||||
rc = pmac_ide_do_suspend(hwif);
|
rc = pmac_ide_do_suspend(hwif);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
pdev->dev.power.power_state = state;
|
pdev->dev.power.power_state = mesg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
|
|||||||
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
|
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
|
||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
|
|
||||||
if (state.event > PM_EVENT_ON) {
|
if (1) {
|
||||||
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
|
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
|
||||||
|
|
||||||
cinergyt2_suspend_rc(cinergyt2);
|
cinergyt2_suspend_rc(cinergyt2);
|
||||||
|
@ -17,6 +17,31 @@ config PCI_MSI
|
|||||||
|
|
||||||
If you don't know what to do here, say N.
|
If you don't know what to do here, say N.
|
||||||
|
|
||||||
|
config PCI_MULTITHREAD_PROBE
|
||||||
|
bool "PCI Multi-threaded probe (EXPERIMENTAL)"
|
||||||
|
depends on PCI && EXPERIMENTAL
|
||||||
|
help
|
||||||
|
Say Y here if you want the PCI core to spawn a new thread for
|
||||||
|
every PCI device that is probed. This can cause a huge
|
||||||
|
speedup in boot times on multiprocessor machines, and even a
|
||||||
|
smaller speedup on single processor machines.
|
||||||
|
|
||||||
|
But it can also cause lots of bad things to happen. A number
|
||||||
|
of PCI drivers can not properly handle running in this way,
|
||||||
|
some will just not work properly at all, while others might
|
||||||
|
decide to blow up power supplies with a huge load all at once,
|
||||||
|
so use this option at your own risk.
|
||||||
|
|
||||||
|
It is very unwise to use this option if you are not using a
|
||||||
|
boot process that can handle devices being created in any
|
||||||
|
order. A program that can create persistant block and network
|
||||||
|
device names (like udev) is a good idea if you wish to use
|
||||||
|
this option.
|
||||||
|
|
||||||
|
Again, use this option at your own risk, you have been warned!
|
||||||
|
|
||||||
|
When in doubt, say N.
|
||||||
|
|
||||||
config PCI_DEBUG
|
config PCI_DEBUG
|
||||||
bool "PCI Debugging"
|
bool "PCI Debugging"
|
||||||
depends on PCI && DEBUG_KERNEL
|
depends on PCI && DEBUG_KERNEL
|
||||||
|
@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
|
|||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
err("%s: Notification handler removal failed\n", __FUNCTION__);
|
err("%s: Notification handler removal failed\n", __FUNCTION__);
|
||||||
/* remove the /sys entries */
|
/* remove the /sys entries */
|
||||||
if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
|
sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
|
||||||
err("%s: removal of sysfs file apci_table failed\n",
|
|
||||||
__FUNCTION__);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(ibm_acpiphp_init);
|
module_init(ibm_acpiphp_init);
|
||||||
|
@ -17,6 +17,16 @@
|
|||||||
* Registration of PCI drivers and handling of hot-pluggable devices.
|
* Registration of PCI drivers and handling of hot-pluggable devices.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* multithreaded probe logic */
|
||||||
|
static int pci_multithread_probe =
|
||||||
|
#ifdef CONFIG_PCI_MULTITHREAD_PROBE
|
||||||
|
1;
|
||||||
|
#else
|
||||||
|
0;
|
||||||
|
#endif
|
||||||
|
__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
|
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
|
||||||
*/
|
*/
|
||||||
@ -279,6 +289,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_device_suspend_late(struct device * dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct pci_dev * pci_dev = to_pci_dev(dev);
|
||||||
|
struct pci_driver * drv = pci_dev->driver;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (drv && drv->suspend_late) {
|
||||||
|
i = drv->suspend_late(pci_dev, state);
|
||||||
|
suspend_report_result(drv->suspend_late, i);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default resume method for devices that have no driver provided resume,
|
* Default resume method for devices that have no driver provided resume,
|
||||||
@ -313,6 +335,17 @@ static int pci_device_resume(struct device * dev)
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_device_resume_early(struct device * dev)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
struct pci_dev * pci_dev = to_pci_dev(dev);
|
||||||
|
struct pci_driver * drv = pci_dev->driver;
|
||||||
|
|
||||||
|
if (drv && drv->resume_early)
|
||||||
|
error = drv->resume_early(pci_dev);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
static void pci_device_shutdown(struct device *dev)
|
static void pci_device_shutdown(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
@ -385,6 +418,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
|
|||||||
drv->driver.bus = &pci_bus_type;
|
drv->driver.bus = &pci_bus_type;
|
||||||
drv->driver.owner = owner;
|
drv->driver.owner = owner;
|
||||||
drv->driver.kobj.ktype = &pci_driver_kobj_type;
|
drv->driver.kobj.ktype = &pci_driver_kobj_type;
|
||||||
|
drv->driver.multithread_probe = pci_multithread_probe;
|
||||||
|
|
||||||
spin_lock_init(&drv->dynids.lock);
|
spin_lock_init(&drv->dynids.lock);
|
||||||
INIT_LIST_HEAD(&drv->dynids.list);
|
INIT_LIST_HEAD(&drv->dynids.list);
|
||||||
@ -509,8 +543,10 @@ struct bus_type pci_bus_type = {
|
|||||||
.probe = pci_device_probe,
|
.probe = pci_device_probe,
|
||||||
.remove = pci_device_remove,
|
.remove = pci_device_remove,
|
||||||
.suspend = pci_device_suspend,
|
.suspend = pci_device_suspend,
|
||||||
.shutdown = pci_device_shutdown,
|
.suspend_late = pci_device_suspend_late,
|
||||||
|
.resume_early = pci_device_resume_early,
|
||||||
.resume = pci_device_resume,
|
.resume = pci_device_resume,
|
||||||
|
.shutdown = pci_device_shutdown,
|
||||||
.dev_attrs = pci_dev_attrs,
|
.dev_attrs = pci_dev_attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
|
|||||||
case PM_EVENT_ON:
|
case PM_EVENT_ON:
|
||||||
return PCI_D0;
|
return PCI_D0;
|
||||||
case PM_EVENT_FREEZE:
|
case PM_EVENT_FREEZE:
|
||||||
|
case PM_EVENT_PRETHAW:
|
||||||
|
/* REVISIT both freeze and pre-thaw "should" use D0 */
|
||||||
case PM_EVENT_SUSPEND:
|
case PM_EVENT_SUSPEND:
|
||||||
return PCI_D3hot;
|
return PCI_D3hot;
|
||||||
default:
|
default:
|
||||||
printk("They asked me for state %d\n", state.event);
|
printk("Unrecognized suspend event %d\n", state.event);
|
||||||
BUG();
|
BUG();
|
||||||
}
|
}
|
||||||
return PCI_D0;
|
return PCI_D0;
|
||||||
|
@ -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);
|
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
|
||||||
msleep(10);
|
msleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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);
|
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
|
||||||
unsigned long flags;
|
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;
|
return 0;
|
||||||
|
|
||||||
scsi_block_requests(ms->host);
|
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);
|
disable_irq(ms->meshintr);
|
||||||
set_mesh_power(ms, 0);
|
set_mesh_power(ms, 0);
|
||||||
|
|
||||||
mdev->ofdev.dev.power.power_state = state;
|
mdev->ofdev.dev.power.power_state = mesg;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
|
|||||||
(void) usb_hcd_pci_resume (dev);
|
(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",
|
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
|
||||||
hcd->state);
|
hcd->state);
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
|
@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
|||||||
writel (0, &ehci->regs->intr_enable);
|
writel (0, &ehci->regs->intr_enable);
|
||||||
(void)readl(&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);
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
bail:
|
bail:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
|
@ -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);
|
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
|
||||||
(void)ohci_readl(ohci, &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);
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
bail:
|
bail:
|
||||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||||
|
@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
|
|||||||
struct sl811 *sl811 = hcd_to_sl811(hcd);
|
struct sl811 *sl811 = hcd_to_sl811(hcd);
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
if (state.event == PM_EVENT_FREEZE)
|
switch (state.event) {
|
||||||
|
case PM_EVENT_FREEZE:
|
||||||
retval = sl811h_bus_suspend(hcd);
|
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);
|
port_power(sl811, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (retval == 0)
|
if (retval == 0)
|
||||||
dev->dev.power.power_state = state;
|
dev->dev.power.power_state = state;
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
|
|||||||
|
|
||||||
/* FIXME: Enable non-PME# remote wakeup? */
|
/* 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:
|
done_okay:
|
||||||
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||||||
done:
|
done:
|
||||||
|
@ -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 fb_info *info = pci_get_drvdata(pdev);
|
||||||
struct radeonfb_info *rinfo = info->par;
|
struct radeonfb_info *rinfo = info->par;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (state.event == pdev->dev.power.power_state.event)
|
if (mesg.event == pdev->dev.power.power_state.event)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
|
printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
|
||||||
pci_name(pdev), state.event);
|
pci_name(pdev), mesg.event);
|
||||||
|
|
||||||
/* For suspend-to-disk, we cheat here. We don't suspend anything and
|
/* 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
|
* let fbcon continue drawing until we are all set. That shouldn't
|
||||||
* really cause any problem at this point, provided that the wakeup
|
* really cause any problem at this point, provided that the wakeup
|
||||||
* code knows that any state in memory may not match the HW
|
* 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;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
acquire_console_sem();
|
acquire_console_sem();
|
||||||
|
|
||||||
@ -2706,7 +2709,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
|||||||
release_console_sem();
|
release_console_sem();
|
||||||
|
|
||||||
done:
|
done:
|
||||||
pdev->dev.power.power_state = state;
|
pdev->dev.power.power_state = mesg;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = {
|
|||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* Power Management *
|
* 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 fb_info *info = pci_get_drvdata(dev);
|
||||||
struct i810fb_par *par = info->par;
|
struct i810fb_par *par = info->par;
|
||||||
|
|
||||||
par->cur_state = state.event;
|
par->cur_state = mesg.event;
|
||||||
|
|
||||||
if (state.event == PM_EVENT_FREEZE) {
|
switch (mesg.event) {
|
||||||
dev->dev.power.power_state = state;
|
case PM_EVENT_FREEZE:
|
||||||
|
case PM_EVENT_PRETHAW:
|
||||||
|
dev->dev.power.power_state = mesg;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
|
|||||||
|
|
||||||
pci_save_state(dev);
|
pci_save_state(dev);
|
||||||
pci_disable_device(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();
|
release_console_sem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -950,24 +950,25 @@ static struct fb_ops nvidia_fb_ops = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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 fb_info *info = pci_get_drvdata(dev);
|
||||||
struct nvidia_par *par = info->par;
|
struct nvidia_par *par = info->par;
|
||||||
|
|
||||||
|
if (mesg.event == PM_EVENT_PRETHAW)
|
||||||
|
mesg.event = PM_EVENT_FREEZE;
|
||||||
acquire_console_sem();
|
acquire_console_sem();
|
||||||
par->pm_state = state.event;
|
par->pm_state = mesg.event;
|
||||||
|
|
||||||
if (state.event == PM_EVENT_FREEZE) {
|
if (mesg.event == PM_EVENT_SUSPEND) {
|
||||||
dev->dev.power.power_state = state;
|
|
||||||
} else {
|
|
||||||
fb_set_suspend(info, 1);
|
fb_set_suspend(info, 1);
|
||||||
nvidiafb_blank(FB_BLANK_POWERDOWN, info);
|
nvidiafb_blank(FB_BLANK_POWERDOWN, info);
|
||||||
nvidia_write_regs(par, &par->SavedReg);
|
nvidia_write_regs(par, &par->SavedReg);
|
||||||
pci_save_state(dev);
|
pci_save_state(dev);
|
||||||
pci_disable_device(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();
|
release_console_sem();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -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 fb_info *info = pci_get_drvdata(dev);
|
||||||
struct savagefb_par *par = info->par;
|
struct savagefb_par *par = info->par;
|
||||||
|
|
||||||
DBG("savagefb_suspend");
|
DBG("savagefb_suspend");
|
||||||
|
|
||||||
|
if (mesg.event == PM_EVENT_PRETHAW)
|
||||||
par->pm_state = state.event;
|
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
|
* For PM_EVENT_FREEZE, do not power down so the console
|
||||||
* can remain active.
|
* can remain active.
|
||||||
*/
|
*/
|
||||||
if (state.event == PM_EVENT_FREEZE) {
|
if (mesg.event == PM_EVENT_FREEZE)
|
||||||
dev->dev.power.power_state = state;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
acquire_console_sem();
|
acquire_console_sem();
|
||||||
fb_set_suspend(info, 1);
|
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);
|
savage_disable_mmio(par);
|
||||||
pci_save_state(dev);
|
pci_save_state(dev);
|
||||||
pci_disable_device(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();
|
release_console_sem();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -55,12 +55,11 @@ static u64 debugfs_u8_get(void *data)
|
|||||||
DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
|
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.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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.
|
* 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
|
* @value: a pointer to the variable that the file should read to and write
|
||||||
* from.
|
* 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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_u8(const char *name, mode_t mode,
|
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");
|
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.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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.
|
* 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
|
* @value: a pointer to the variable that the file should read to and write
|
||||||
* from.
|
* 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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_u16(const char *name, mode_t mode,
|
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");
|
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.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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.
|
* 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
|
* @value: a pointer to the variable that the file should read to and write
|
||||||
* from.
|
* 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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_u32(const char *name, mode_t mode,
|
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.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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.
|
* 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
|
* @value: a pointer to the variable that the file should read to and write
|
||||||
* from.
|
* 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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
|
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
|
* debugfs_create_blob - create a debugfs file that is used to read and write a binary blob
|
||||||
* used to read and write a binary blob.
|
|
||||||
*
|
|
||||||
* @name: a pointer to a string containing the name of the file to create.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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.
|
* file will be created in the root of the debugfs filesystem.
|
||||||
* @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer
|
* @blob: a pointer to a struct debugfs_blob_wrapper which contains a pointer
|
||||||
* to the blob data and the size of the data.
|
* 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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_blob(const char *name, mode_t mode,
|
struct dentry *debugfs_create_blob(const char *name, mode_t mode,
|
||||||
|
@ -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
|
* debugfs_create_file - create a file in the debugfs filesystem
|
||||||
*
|
|
||||||
* @name: a pointer to a string containing the name of the file to create.
|
* @name: a pointer to a string containing the name of the file to create.
|
||||||
* @mode: the permission that the file should have
|
* @mode: the permission that the file should have
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_file(const char *name, mode_t mode,
|
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
|
* debugfs_create_dir - create a directory in the debugfs filesystem
|
||||||
*
|
|
||||||
* @name: a pointer to a string containing the name of the directory to
|
* @name: a pointer to a string containing the name of the directory to
|
||||||
* create.
|
* create.
|
||||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
* @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
|
* 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
|
* 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,
|
* 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
|
* 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.
|
* code.
|
||||||
*/
|
*/
|
||||||
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
|
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
|
* 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
|
* @dentry: a pointer to a the dentry of the file or directory to be
|
||||||
* removed.
|
* removed.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <linux/acct.h>
|
#include <linux/acct.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/sysfs.h>
|
||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/namespace.h>
|
#include <linux/namespace.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
@ -28,15 +29,6 @@
|
|||||||
|
|
||||||
extern int __init init_rootfs(void);
|
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 */
|
/* spinlock for vfsmount related operations, inplace of dcache_lock */
|
||||||
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
|
__cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -176,7 +177,6 @@ const struct file_operations bin_fops = {
|
|||||||
* sysfs_create_bin_file - create binary file for object.
|
* sysfs_create_bin_file - create binary file for object.
|
||||||
* @kobj: object.
|
* @kobj: object.
|
||||||
* @attr: attribute descriptor.
|
* @attr: attribute descriptor.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
|
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.
|
* sysfs_remove_bin_file - remove binary file for object.
|
||||||
* @kobj: object.
|
* @kobj: object.
|
||||||
* @attr: attribute descriptor.
|
* @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);
|
if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
|
||||||
return 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);
|
EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
|
||||||
|
@ -43,7 +43,7 @@ static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd,
|
|||||||
|
|
||||||
memset(sd, 0, sizeof(*sd));
|
memset(sd, 0, sizeof(*sd));
|
||||||
atomic_set(&sd->s_count, 1);
|
atomic_set(&sd->s_count, 1);
|
||||||
atomic_set(&sd->s_event, 0);
|
atomic_set(&sd->s_event, 1);
|
||||||
INIT_LIST_HEAD(&sd->s_children);
|
INIT_LIST_HEAD(&sd->s_children);
|
||||||
list_add(&sd->s_sibling, &parent_sd->s_children);
|
list_add(&sd->s_sibling, &parent_sd->s_children);
|
||||||
sd->s_element = element;
|
sd->s_element = element;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/backing-dev.h>
|
#include <linux/backing-dev.h>
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
#include "sysfs.h"
|
#include "sysfs.h"
|
||||||
|
|
||||||
extern struct super_block * sysfs_sb;
|
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 * sd;
|
||||||
struct sysfs_dirent * parent_sd;
|
struct sysfs_dirent * parent_sd;
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
if (!dir)
|
if (!dir)
|
||||||
return;
|
return -ENOENT;
|
||||||
|
|
||||||
if (dir->d_inode == NULL)
|
if (dir->d_inode == NULL)
|
||||||
/* no inode means this hasn't been made visible yet */
|
/* no inode means this hasn't been made visible yet */
|
||||||
return;
|
return -ENOENT;
|
||||||
|
|
||||||
parent_sd = dir->d_fsdata;
|
parent_sd = dir->d_fsdata;
|
||||||
mutex_lock(&dir->d_inode->i_mutex);
|
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);
|
list_del_init(&sd->s_sibling);
|
||||||
sysfs_drop_dentry(sd, dir);
|
sysfs_drop_dentry(sd, dir);
|
||||||
sysfs_put(sd);
|
sysfs_put(sd);
|
||||||
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&dir->d_inode->i_mutex);
|
mutex_unlock(&dir->d_inode->i_mutex);
|
||||||
|
|
||||||
|
return found ? 0 : -ENOENT;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
@ -82,10 +83,19 @@ exit1:
|
|||||||
*/
|
*/
|
||||||
int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)
|
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;
|
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);
|
mutex_lock(&dentry->d_inode->i_mutex);
|
||||||
if (!sysfs_dirent_exist(dentry->d_fsdata, name))
|
if (!sysfs_dirent_exist(dentry->d_fsdata, name))
|
||||||
|
@ -10,7 +10,7 @@ extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *,
|
|||||||
umode_t, int);
|
umode_t, int);
|
||||||
|
|
||||||
extern int sysfs_add_file(struct dentry *, const struct attribute *, 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 struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name);
|
||||||
|
|
||||||
extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
|
extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **);
|
||||||
|
@ -99,6 +99,11 @@ extern void __chk_io_ptr(void __iomem *);
|
|||||||
#define __must_check
|
#define __must_check
|
||||||
#endif
|
#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,
|
* 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.
|
* as well as force them to be emitted to the assembly file.
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/klist.h>
|
#include <linux/klist.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
@ -51,14 +52,17 @@ struct bus_type {
|
|||||||
int (*probe)(struct device * dev);
|
int (*probe)(struct device * dev);
|
||||||
int (*remove)(struct device * dev);
|
int (*remove)(struct device * dev);
|
||||||
void (*shutdown)(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_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 */
|
/* 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,
|
struct device * bus_find_device(struct bus_type *bus, struct device *start,
|
||||||
void *data, int (*match)(struct device *, void *));
|
void *data, int (*match)(struct device *, void *));
|
||||||
|
|
||||||
int bus_for_each_drv(struct bus_type * bus, struct device_driver * start,
|
int __must_check bus_for_each_drv(struct bus_type *bus,
|
||||||
void * data, int (*fn)(struct device_driver *, void *));
|
struct device_driver *start, void *data,
|
||||||
|
int (*fn)(struct device_driver *, void *));
|
||||||
|
|
||||||
/* driverfs interface for exporting bus attributes */
|
/* driverfs interface for exporting bus attributes */
|
||||||
|
|
||||||
@ -82,7 +86,8 @@ struct bus_attribute {
|
|||||||
#define BUS_ATTR(_name,_mode,_show,_store) \
|
#define BUS_ATTR(_name,_mode,_show,_store) \
|
||||||
struct bus_attribute bus_attr_##_name = __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 *);
|
extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
||||||
|
|
||||||
struct device_driver {
|
struct device_driver {
|
||||||
@ -101,16 +106,18 @@ struct device_driver {
|
|||||||
void (*shutdown) (struct device * dev);
|
void (*shutdown) (struct device * dev);
|
||||||
int (*suspend) (struct device * dev, pm_message_t state);
|
int (*suspend) (struct device * dev, pm_message_t state);
|
||||||
int (*resume) (struct device * dev);
|
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 void driver_unregister(struct device_driver * drv);
|
||||||
|
|
||||||
extern struct device_driver * get_driver(struct device_driver * drv);
|
extern struct device_driver * get_driver(struct device_driver * drv);
|
||||||
extern void put_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 struct device_driver *driver_find(const char *name, struct bus_type *bus);
|
||||||
|
extern int driver_probe_done(void);
|
||||||
|
|
||||||
/* driverfs interface for exporting driver attributes */
|
/* driverfs interface for exporting driver attributes */
|
||||||
|
|
||||||
@ -123,16 +130,17 @@ struct driver_attribute {
|
|||||||
#define DRIVER_ATTR(_name,_mode,_show,_store) \
|
#define DRIVER_ATTR(_name,_mode,_show,_store) \
|
||||||
struct driver_attribute driver_attr_##_name = __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 void driver_remove_file(struct device_driver *, struct driver_attribute *);
|
||||||
|
|
||||||
extern int driver_for_each_device(struct device_driver * drv, struct device * start,
|
extern int __must_check driver_for_each_device(struct device_driver * drv,
|
||||||
void * data, int (*fn)(struct device *, void *));
|
struct device *start, void *data,
|
||||||
|
int (*fn)(struct device *, void *));
|
||||||
struct device * driver_find_device(struct device_driver *drv,
|
struct device * driver_find_device(struct device_driver *drv,
|
||||||
struct device *start, void *data,
|
struct device *start, void *data,
|
||||||
int (*match)(struct device *, void *));
|
int (*match)(struct device *, void *));
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* device classes
|
* device classes
|
||||||
*/
|
*/
|
||||||
@ -146,17 +154,26 @@ struct class {
|
|||||||
struct list_head interfaces;
|
struct list_head interfaces;
|
||||||
struct semaphore sem; /* locks both the children and interfaces lists */
|
struct semaphore sem; /* locks both the children and interfaces lists */
|
||||||
|
|
||||||
|
struct kobject *virtual_dir;
|
||||||
|
|
||||||
struct class_attribute * class_attrs;
|
struct class_attribute * class_attrs;
|
||||||
struct class_device_attribute * class_dev_attrs;
|
struct class_device_attribute * class_dev_attrs;
|
||||||
|
struct device_attribute * dev_attrs;
|
||||||
|
|
||||||
int (*uevent)(struct class_device *dev, char **envp,
|
int (*uevent)(struct class_device *dev, char **envp,
|
||||||
int num_envp, char *buffer, int buffer_size);
|
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 (*release)(struct class_device *dev);
|
||||||
void (*class_release)(struct class *class);
|
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 *);
|
extern void class_unregister(struct class *);
|
||||||
|
|
||||||
|
|
||||||
@ -169,7 +186,8 @@ struct class_attribute {
|
|||||||
#define CLASS_ATTR(_name,_mode,_show,_store) \
|
#define CLASS_ATTR(_name,_mode,_show,_store) \
|
||||||
struct class_attribute class_attr_##_name = __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 *);
|
extern void class_remove_file(struct class *, const struct class_attribute *);
|
||||||
|
|
||||||
struct class_device_attribute {
|
struct class_device_attribute {
|
||||||
@ -182,7 +200,7 @@ struct class_device_attribute {
|
|||||||
struct class_device_attribute class_device_attr_##_name = \
|
struct class_device_attribute class_device_attr_##_name = \
|
||||||
__ATTR(_name,_mode,_show,_store)
|
__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 *);
|
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_unregister(struct class_device *);
|
||||||
extern void class_device_initialize(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 void class_device_del(struct class_device *);
|
||||||
|
|
||||||
extern int class_device_rename(struct class_device *, char *);
|
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 *,
|
extern void class_device_remove_file(struct class_device *,
|
||||||
const struct class_device_attribute *);
|
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 *);
|
struct bin_attribute *);
|
||||||
extern void class_device_remove_bin_file(struct class_device *,
|
extern void class_device_remove_bin_file(struct class_device *,
|
||||||
struct bin_attribute *);
|
struct bin_attribute *);
|
||||||
@ -266,22 +284,23 @@ struct class_interface {
|
|||||||
|
|
||||||
int (*add) (struct class_device *, struct class_interface *);
|
int (*add) (struct class_device *, struct class_interface *);
|
||||||
void (*remove) (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 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 void class_destroy(struct class *cls);
|
||||||
extern struct class_device *class_device_create(struct class *cls,
|
extern struct class_device *class_device_create(struct class *cls,
|
||||||
struct class_device *parent,
|
struct class_device *parent,
|
||||||
dev_t devt,
|
dev_t devt,
|
||||||
struct device *device,
|
struct device *device,
|
||||||
char *fmt, ...)
|
const char *fmt, ...)
|
||||||
__attribute__((format(printf,5,6)));
|
__attribute__((format(printf,5,6)));
|
||||||
extern void class_device_destroy(struct class *cls, dev_t devt);
|
extern void class_device_destroy(struct class *cls, dev_t devt);
|
||||||
|
|
||||||
|
|
||||||
/* interface for exporting device attributes */
|
/* interface for exporting device attributes */
|
||||||
struct device_attribute {
|
struct device_attribute {
|
||||||
struct attribute attr;
|
struct attribute attr;
|
||||||
@ -294,8 +313,13 @@ struct device_attribute {
|
|||||||
#define DEVICE_ATTR(_name,_mode,_show,_store) \
|
#define DEVICE_ATTR(_name,_mode,_show,_store) \
|
||||||
struct device_attribute dev_attr_##_name = __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 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 device {
|
||||||
struct klist klist_children;
|
struct klist klist_children;
|
||||||
struct klist_node knode_parent; /* node in sibling list */
|
struct klist_node knode_parent; /* node in sibling list */
|
||||||
@ -305,6 +329,7 @@ struct device {
|
|||||||
|
|
||||||
struct kobject kobj;
|
struct kobject kobj;
|
||||||
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
|
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
|
||||||
|
unsigned is_registered:1;
|
||||||
struct device_attribute uevent_attr;
|
struct device_attribute uevent_attr;
|
||||||
struct device_attribute *devt_attr;
|
struct device_attribute *devt_attr;
|
||||||
|
|
||||||
@ -338,6 +363,7 @@ struct device {
|
|||||||
struct list_head node;
|
struct list_head node;
|
||||||
struct class *class; /* optional*/
|
struct class *class; /* optional*/
|
||||||
dev_t devt; /* dev_t, creates the sysfs "dev" */
|
dev_t devt; /* dev_t, creates the sysfs "dev" */
|
||||||
|
struct attribute_group **groups; /* optional groups */
|
||||||
|
|
||||||
void (*release)(struct device * dev);
|
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)
|
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
|
* 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_unregister(struct device * dev);
|
||||||
extern void device_initialize(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 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 *));
|
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
|
* Manual binding of a device to driver. See drivers/base/bus.c
|
||||||
* for information on use.
|
* 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 void device_release_driver(struct device * dev);
|
||||||
extern int device_attach(struct device * dev);
|
extern int __must_check device_attach(struct device * dev);
|
||||||
extern void driver_attach(struct device_driver * drv);
|
extern int __must_check driver_attach(struct device_driver *drv);
|
||||||
extern void device_reprobe(struct device *dev);
|
extern int __must_check device_reprobe(struct device *dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Easy functions for dynamically creating devices on the fly
|
* Easy functions for dynamically creating devices on the fly
|
||||||
*/
|
*/
|
||||||
extern struct device *device_create(struct class *cls, struct device *parent,
|
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)));
|
__attribute__((format(printf,4,5)));
|
||||||
extern void device_destroy(struct class *cls, dev_t devt);
|
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
|
* Platform "fixup" functions - allow the platform to have their say
|
||||||
* about devices and actions that the general device layer doesn't
|
* about devices and actions that the general device layer doesn't
|
||||||
@ -412,7 +441,7 @@ extern void device_shutdown(void);
|
|||||||
|
|
||||||
|
|
||||||
/* drivers/base/firmware.c */
|
/* drivers/base/firmware.c */
|
||||||
extern int firmware_register(struct subsystem *);
|
extern int __must_check firmware_register(struct subsystem *);
|
||||||
extern void firmware_unregister(struct subsystem *);
|
extern void firmware_unregister(struct subsystem *);
|
||||||
|
|
||||||
/* debugging and troubleshooting/diagnostic helpers. */
|
/* debugging and troubleshooting/diagnostic helpers. */
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/kref.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_init(struct kobject *);
|
||||||
extern void kobject_cleanup(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 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 void kobject_unregister(struct kobject *);
|
||||||
|
|
||||||
extern struct kobject * kobject_get(struct kobject *);
|
extern struct kobject * kobject_get(struct kobject *);
|
||||||
@ -128,8 +129,8 @@ struct kset {
|
|||||||
|
|
||||||
|
|
||||||
extern void kset_init(struct kset * k);
|
extern void kset_init(struct kset * k);
|
||||||
extern int kset_add(struct kset * k);
|
extern int __must_check kset_add(struct kset * k);
|
||||||
extern int kset_register(struct kset * k);
|
extern int __must_check kset_register(struct kset * k);
|
||||||
extern void kset_unregister(struct kset * k);
|
extern void kset_unregister(struct kset * k);
|
||||||
|
|
||||||
static inline struct kset * to_kset(struct kobject * kobj)
|
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
|
(obj)->subsys.kset.kobj.kset = &(_subsys).kset
|
||||||
|
|
||||||
extern void subsystem_init(struct subsystem *);
|
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 *);
|
extern void subsystem_unregister(struct subsystem *);
|
||||||
|
|
||||||
static inline struct subsystem * subsys_get(struct subsystem * s)
|
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);
|
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)
|
#if defined(CONFIG_HOTPLUG)
|
||||||
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
|
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/device.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 */
|
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) */
|
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) (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 (*resume) (struct pci_dev *dev); /* Device woken up */
|
||||||
int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
|
int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
|
||||||
void (*shutdown) (struct pci_dev *dev);
|
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 */
|
extern struct list_head pci_devices; /* list of all devices */
|
||||||
|
|
||||||
void pcibios_fixup_bus(struct pci_bus *);
|
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);
|
char *pcibios_setup (char *str);
|
||||||
|
|
||||||
/* Used only when drivers/pci/setup.c is used */
|
/* 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);
|
return pci_bus_write_config_dword (dev->bus, dev->devfn, where, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pci_enable_device(struct pci_dev *dev);
|
int __must_check pci_enable_device(struct pci_dev *dev);
|
||||||
int pci_enable_device_bars(struct pci_dev *dev, int mask);
|
int __must_check pci_enable_device_bars(struct pci_dev *dev, int mask);
|
||||||
void pci_disable_device(struct pci_dev *dev);
|
void pci_disable_device(struct pci_dev *dev);
|
||||||
void pci_set_master(struct pci_dev *dev);
|
void pci_set_master(struct pci_dev *dev);
|
||||||
#define HAVE_PCI_SET_MWI
|
#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_clear_mwi(struct pci_dev *dev);
|
||||||
void pci_intx(struct pci_dev *dev, int enable);
|
void pci_intx(struct pci_dev *dev, int enable);
|
||||||
int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
|
int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
|
||||||
int pci_set_consistent_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);
|
void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
|
||||||
int pci_assign_resource(struct pci_dev *dev, int i);
|
int __must_check 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_fixed(struct pci_dev *dev, int i);
|
||||||
void pci_restore_bars(struct pci_dev *dev);
|
void pci_restore_bars(struct pci_dev *dev);
|
||||||
|
|
||||||
/* ROM control related routines */
|
/* 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 *),
|
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
|
||||||
int (*)(struct pci_dev *, u8, u8));
|
int (*)(struct pci_dev *, u8, u8));
|
||||||
#define HAVE_PCI_REQ_REGIONS 2
|
#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 *);
|
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);
|
void pci_release_region(struct pci_dev *, int);
|
||||||
|
|
||||||
/* drivers/pci/bus.c */
|
/* drivers/pci/bus.c */
|
||||||
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
|
int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
|
||||||
resource_size_t size, resource_size_t align,
|
struct resource *res, resource_size_t size,
|
||||||
resource_size_t min, unsigned int type_mask,
|
resource_size_t align, resource_size_t min,
|
||||||
void (*alignf)(void *, struct resource *,
|
unsigned int type_mask,
|
||||||
resource_size_t, resource_size_t),
|
void (*alignf)(void *, struct resource *,
|
||||||
void *alignf_data);
|
resource_size_t, resource_size_t),
|
||||||
|
void *alignf_data);
|
||||||
void pci_enable_bridges(struct pci_bus *bus);
|
void pci_enable_bridges(struct pci_bus *bus);
|
||||||
|
|
||||||
/* Proper probing supporting hot-pluggable devices */
|
/* Proper probing supporting hot-pluggable devices */
|
||||||
int __pci_register_driver(struct pci_driver *, struct module *);
|
int __must_check __pci_register_driver(struct pci_driver *, struct module *);
|
||||||
static inline int pci_register_driver(struct pci_driver *driver)
|
static inline int __must_check pci_register_driver(struct pci_driver *driver)
|
||||||
{
|
{
|
||||||
return __pci_register_driver(driver, THIS_MODULE);
|
return __pci_register_driver(driver, THIS_MODULE);
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ struct platform_driver {
|
|||||||
int (*remove)(struct platform_device *);
|
int (*remove)(struct platform_device *);
|
||||||
void (*shutdown)(struct platform_device *);
|
void (*shutdown)(struct platform_device *);
|
||||||
int (*suspend)(struct platform_device *, pm_message_t state);
|
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 *);
|
int (*resume)(struct platform_device *);
|
||||||
struct device_driver driver;
|
struct device_driver driver;
|
||||||
};
|
};
|
||||||
|
@ -142,29 +142,61 @@ typedef struct pm_message {
|
|||||||
} pm_message_t;
|
} pm_message_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are 4 important states driver can be in:
|
* Several driver power state transitions are externally visible, affecting
|
||||||
* ON -- driver is working
|
* the state of pending I/O queues and (for drivers that touch hardware)
|
||||||
* FREEZE -- stop operations and apply whatever policy is applicable to a
|
* interrupts, wakeups, DMA, and other hardware state. There may also be
|
||||||
* suspended driver of that class, freeze queues for block like IDE
|
* internal transitions to various low power modes, which are transparent
|
||||||
* does, drop packets for ethernet, etc... stop DMA engine too etc...
|
* to the rest of the driver stack (such as a driver that's ON gating off
|
||||||
* so a consistent image can be saved; but do not power any hardware
|
* clocks which are not in active use).
|
||||||
* down.
|
|
||||||
* SUSPEND - like FREEZE, but hardware is doing as much powersaving as
|
|
||||||
* possible. Roughly pci D3.
|
|
||||||
*
|
*
|
||||||
* Unfortunately, current drivers only recognize numeric values 0 (ON) and 3
|
* One transition is triggered by resume(), after a suspend() call; the
|
||||||
* (SUSPEND). We'll need to fix the drivers. So yes, putting 3 to all different
|
* message is implicit:
|
||||||
* 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
|
* ON Driver starts working again, responding to hardware events
|
||||||
* typedef struct pm_message_t { int event; int flags; } pm_message_t
|
* and software requests. The hardware may have gone through
|
||||||
* or something similar soon.
|
* 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_ON 0
|
||||||
#define PM_EVENT_FREEZE 1
|
#define PM_EVENT_FREEZE 1
|
||||||
#define PM_EVENT_SUSPEND 2
|
#define PM_EVENT_SUSPEND 2
|
||||||
|
#define PM_EVENT_PRETHAW 3
|
||||||
|
|
||||||
#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
|
#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_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
|
||||||
#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
|
#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 suspend_disk_method_t pm_disk_mode;
|
||||||
|
|
||||||
extern int device_suspend(pm_message_t state);
|
extern int device_suspend(pm_message_t state);
|
||||||
|
extern int device_prepare_suspend(pm_message_t state);
|
||||||
|
|
||||||
#define device_set_wakeup_enable(dev,val) \
|
#define device_set_wakeup_enable(dev,val) \
|
||||||
((dev)->power.should_wakeup = !!(val))
|
((dev)->power.should_wakeup = !!(val))
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#ifndef _SYSFS_H_
|
#ifndef _SYSFS_H_
|
||||||
#define _SYSFS_H_
|
#define _SYSFS_H_
|
||||||
|
|
||||||
|
#include <linux/compiler.h>
|
||||||
#include <asm/atomic.h>
|
#include <asm/atomic.h>
|
||||||
|
|
||||||
struct kobject;
|
struct kobject;
|
||||||
@ -86,40 +87,44 @@ struct sysfs_dirent {
|
|||||||
|
|
||||||
#ifdef CONFIG_SYSFS
|
#ifdef CONFIG_SYSFS
|
||||||
|
|
||||||
extern int
|
extern int __must_check
|
||||||
sysfs_create_dir(struct kobject *);
|
sysfs_create_dir(struct kobject *);
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
sysfs_remove_dir(struct kobject *);
|
sysfs_remove_dir(struct kobject *);
|
||||||
|
|
||||||
extern int
|
extern int __must_check
|
||||||
sysfs_rename_dir(struct kobject *, const char *new_name);
|
sysfs_rename_dir(struct kobject *, const char *new_name);
|
||||||
|
|
||||||
extern int
|
extern int __must_check
|
||||||
sysfs_create_file(struct kobject *, const struct attribute *);
|
sysfs_create_file(struct kobject *, const struct attribute *);
|
||||||
|
|
||||||
extern int
|
extern int __must_check
|
||||||
sysfs_update_file(struct kobject *, const struct attribute *);
|
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);
|
sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode);
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
sysfs_remove_file(struct kobject *, const struct attribute *);
|
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);
|
sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name);
|
||||||
|
|
||||||
extern void
|
extern void
|
||||||
sysfs_remove_link(struct kobject *, const char * name);
|
sysfs_remove_link(struct kobject *, const char * name);
|
||||||
|
|
||||||
int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr);
|
int __must_check sysfs_create_bin_file(struct kobject *kobj,
|
||||||
int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr);
|
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_remove_group(struct kobject *, const struct attribute_group *);
|
||||||
void sysfs_notify(struct kobject * k, char *dir, char *attr);
|
void sysfs_notify(struct kobject * k, char *dir, char *attr);
|
||||||
|
|
||||||
|
extern int __must_check sysfs_init(void);
|
||||||
|
|
||||||
#else /* CONFIG_SYSFS */
|
#else /* CONFIG_SYSFS */
|
||||||
|
|
||||||
static inline int sysfs_create_dir(struct kobject * k)
|
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 /* CONFIG_SYSFS */
|
||||||
|
|
||||||
#endif /* _SYSFS_H_ */
|
#endif /* _SYSFS_H_ */
|
||||||
|
@ -341,7 +341,7 @@ extern int video_usercopy(struct inode *inode, struct file *file,
|
|||||||
extern struct video_device* video_devdata(struct file*);
|
extern struct video_device* video_devdata(struct file*);
|
||||||
|
|
||||||
#define to_video_device(cd) container_of(cd, struct video_device, class_dev)
|
#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,
|
video_device_create_file(struct video_device *vfd,
|
||||||
struct class_device_attribute *attr)
|
struct class_device_attribute *attr)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <linux/security.h>
|
#include <linux/security.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
#include <linux/nfs_fs.h>
|
#include <linux/nfs_fs.h>
|
||||||
#include <linux/nfs_fs_sb.h>
|
#include <linux/nfs_fs_sb.h>
|
||||||
@ -403,6 +404,10 @@ void __init prepare_namespace(void)
|
|||||||
ssleep(root_delay);
|
ssleep(root_delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* wait for the known devices to complete their probing */
|
||||||
|
while (driver_probe_done() != 0)
|
||||||
|
msleep(100);
|
||||||
|
|
||||||
md_run_setup();
|
md_run_setup();
|
||||||
|
|
||||||
if (saved_root_name[0]) {
|
if (saved_root_name[0]) {
|
||||||
|
@ -64,6 +64,17 @@ config PM_TRACE
|
|||||||
CAUTION: this option will cause your machine's real-time clock to be
|
CAUTION: this option will cause your machine's real-time clock to be
|
||||||
set to an invalid time after a resume.
|
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
|
config SOFTWARE_SUSPEND
|
||||||
bool "Software Suspend"
|
bool "Software Suspend"
|
||||||
|
@ -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.
|
* 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");
|
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");
|
printk("Some devices failed to suspend\n");
|
||||||
swsusp_free();
|
swsusp_free();
|
||||||
goto Thaw;
|
goto Thaw;
|
||||||
|
@ -247,6 +247,9 @@ int swsusp_suspend(void)
|
|||||||
restore_processor_state();
|
restore_processor_state();
|
||||||
Restore_highmem:
|
Restore_highmem:
|
||||||
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();
|
device_power_up();
|
||||||
Enable_irqs:
|
Enable_irqs:
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
@ -256,8 +259,12 @@ Enable_irqs:
|
|||||||
int swsusp_resume(void)
|
int swsusp_resume(void)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
local_irq_disable();
|
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");
|
printk(KERN_ERR "Some devices failed to power down, very bad\n");
|
||||||
/* We'll ignore saved state, but this gets preempt count (etc) right */
|
/* We'll ignore saved state, but this gets preempt count (etc) right */
|
||||||
save_processor_state();
|
save_processor_state();
|
||||||
|
@ -196,7 +196,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp,
|
|||||||
snapshot_free_unused_memory(&data->handle);
|
snapshot_free_unused_memory(&data->handle);
|
||||||
down(&pm_sem);
|
down(&pm_sem);
|
||||||
pm_prepare_console();
|
pm_prepare_console();
|
||||||
error = device_suspend(PMSG_FREEZE);
|
error = device_suspend(PMSG_PRETHAW);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
error = swsusp_resume();
|
error = swsusp_resume();
|
||||||
device_resume();
|
device_resume();
|
||||||
|
@ -8,6 +8,13 @@ config PRINTK_TIME
|
|||||||
operations. This is useful for identifying long delays
|
operations. This is useful for identifying long delays
|
||||||
in kernel startup.
|
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
|
config MAGIC_SYSRQ
|
||||||
bool "Magic SysRq key"
|
bool "Magic SysRq key"
|
||||||
|
26
lib/klist.c
26
lib/klist.c
@ -123,12 +123,10 @@ EXPORT_SYMBOL_GPL(klist_add_tail);
|
|||||||
static void klist_release(struct kref * kref)
|
static void klist_release(struct kref * kref)
|
||||||
{
|
{
|
||||||
struct klist_node * n = container_of(kref, struct klist_node, n_ref);
|
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);
|
list_del(&n->n_node);
|
||||||
complete(&n->n_removed);
|
complete(&n->n_removed);
|
||||||
n->n_klist = NULL;
|
n->n_klist = NULL;
|
||||||
if (put)
|
|
||||||
put(n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int klist_dec_and_del(struct klist_node * 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)
|
void klist_del(struct klist_node * n)
|
||||||
{
|
{
|
||||||
struct klist * k = n->n_klist;
|
struct klist * k = n->n_klist;
|
||||||
|
void (*put)(struct klist_node *) = k->put;
|
||||||
|
|
||||||
spin_lock(&k->k_lock);
|
spin_lock(&k->k_lock);
|
||||||
klist_dec_and_del(n);
|
if (!klist_dec_and_del(n))
|
||||||
|
put = NULL;
|
||||||
spin_unlock(&k->k_lock);
|
spin_unlock(&k->k_lock);
|
||||||
|
if (put)
|
||||||
|
put(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(klist_del);
|
EXPORT_SYMBOL_GPL(klist_del);
|
||||||
@ -161,10 +163,7 @@ EXPORT_SYMBOL_GPL(klist_del);
|
|||||||
|
|
||||||
void klist_remove(struct klist_node * n)
|
void klist_remove(struct klist_node * n)
|
||||||
{
|
{
|
||||||
struct klist * k = n->n_klist;
|
klist_del(n);
|
||||||
spin_lock(&k->k_lock);
|
|
||||||
klist_dec_and_del(n);
|
|
||||||
spin_unlock(&k->k_lock);
|
|
||||||
wait_for_completion(&n->n_removed);
|
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 klist_node * klist_next(struct klist_iter * i)
|
||||||
{
|
{
|
||||||
struct list_head * next;
|
struct list_head * next;
|
||||||
|
struct klist_node * lnode = i->i_cur;
|
||||||
struct klist_node * knode = NULL;
|
struct klist_node * knode = NULL;
|
||||||
|
void (*put)(struct klist_node *) = i->i_klist->put;
|
||||||
|
|
||||||
spin_lock(&i->i_klist->k_lock);
|
spin_lock(&i->i_klist->k_lock);
|
||||||
if (i->i_cur) {
|
if (lnode) {
|
||||||
next = i->i_cur->n_node.next;
|
next = lnode->n_node.next;
|
||||||
klist_dec_and_del(i->i_cur);
|
if (!klist_dec_and_del(lnode))
|
||||||
|
put = NULL;
|
||||||
} else
|
} else
|
||||||
next = i->i_head->next;
|
next = i->i_head->next;
|
||||||
|
|
||||||
@ -275,6 +277,8 @@ struct klist_node * klist_next(struct klist_iter * i)
|
|||||||
}
|
}
|
||||||
i->i_cur = knode;
|
i->i_cur = knode;
|
||||||
spin_unlock(&i->i_klist->k_lock);
|
spin_unlock(&i->i_klist->k_lock);
|
||||||
|
if (put && lnode)
|
||||||
|
put(lnode);
|
||||||
return knode;
|
return knode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +407,7 @@ static struct kobj_type dir_ktype = {
|
|||||||
struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
|
struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
|
||||||
{
|
{
|
||||||
struct kobject *k;
|
struct kobject *k;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -418,7 +419,13 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
|
|||||||
k->parent = parent;
|
k->parent = parent;
|
||||||
k->ktype = &dir_ktype;
|
k->ktype = &dir_ktype;
|
||||||
kobject_set_name(k, name);
|
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;
|
return k;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user