Near the top of kernel/power/suspend.c
note the line:
static struct platform_suspend_ops *suspend_ops;
This means that suspend_ops
is a global variable that is private to this file. This means that in order to be of any use, there will have to be an assignment somewhere in suspend.c
. So let's look for the assignment. Searching through suspend.c
for suspend_ops
we see only one assignment, in the subroutine suspend_set_ops
. Searching for suspend_set_ops
in suspend.c
we see that there are no calls to it. So, the calls must be somewhere else!
We search the entire kernel for suspend_set_ops
:
yba@tavas:~/linux-2.6/linux-2.6.31$ find . -type f | xargs grep suspend_set_ops
./kernel/power/suspend.c: * suspend_set_ops - Set the global suspend method table.
./kernel/power/suspend.c:void suspend_set_ops(struct platform_suspend_ops *ops)
./arch/mips/alchemy/devboards/pm.c: suspend_set_ops(&db1x_pm_ops);
./arch/arm/mach-sa1100/pm.c: suspend_set_ops(&sa11x0_pm_ops);
./arch/arm/plat-s3c/pm.c: suspend_set_ops(&s3c_pm_ops);
./arch/arm/mach-omap2/pm24xx.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-omap2/pm34xx.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-pxa/pm.c: suspend_set_ops(&pxa_pm_ops);
./arch/arm/mach-pxa/sharpsl_pm.c: suspend_set_ops(&sharpsl_pm_ops);
./arch/arm/mach-pxa/sharpsl_pm.c: suspend_set_ops(NULL);
./arch/arm/mach-at91/pm.c: suspend_set_ops(&at91_pm_ops);
./arch/arm/mach-omap1/pm.c: suspend_set_ops(&omap_pm_ops);
./arch/arm/mach-pnx4008/pm.c: suspend_set_ops(&pnx4008_pm_ops);
./arch/sh/kernel/cpu/shmobile/pm.c: suspend_set_ops(&sh_pm_ops);
./arch/sh/boards/mach-hp6xx/pm.c: suspend_set_ops(&hp6x0_pm_ops);
./arch/powerpc/platforms/83xx/suspend.c: suspend_set_ops(&mpc83xx_suspend_ops);
./arch/powerpc/platforms/52xx/mpc52xx_pm.c: suspend_set_ops(&mpc52xx_pm_ops);
./arch/powerpc/platforms/52xx/lite5200_pm.c: suspend_set_ops(&lite5200_pm_ops);
./arch/avr32/mach-at32ap/pm.c: suspend_set_ops(&avr32_pm_ops);
./arch/blackfin/mach-common/pm.c: suspend_set_ops(&bfin_pm_ops);
./include/linux/suspend.h: * suspend_set_ops - set platform dependent suspend operations
./include/linux/suspend.h:extern void suspend_set_ops(struct platform_suspend_ops *ops);
./include/linux/suspend.h:static inline void suspend_set_ops(struct platform_suspend_ops *ops) {}
./drivers/macintosh/via-pmu.c: suspend_set_ops(&pmu_pm_ops);
./drivers/acpi/sleep.c: suspend_set_ops(old_suspend_ordering ?
What we see is that almost all of the calls to suspend_set_ops
are in the architecture-specific directories, except for the two drivers at the end of the output: macintosh/via-pmu.c
and acpi/sleep.c
. So the next step is to look at the architecture-specific code for some platform. We'll use ./arch/powerpc/platforms/83xx/suspend.c
as an example. You should do this yourself for whatever platform you are using. If it is x86, then you probably need to look in ./drivers/acpi/sleep.c
.
Searching for suspend_set_ops
in that file we see that it is called once, in the subroutine pmc_probe
which is static. So we look for calls to pmc_probe
in the same file until we find one that is not static. We find a static declaration of a structure of type `of_platform_driver
called `pmc_driverthat assigns
pmc_probeto element
probe``.
Since the pmc_driver
structure is static, we look for references to it in the same file, and fine one, in the static function pmc_init
. We look for calls to pmc_init
and find one, as a parameter to the module_init
call.
We search for module_init
from the top of the kernel tree, limiting our search slightly:
find . -type f | xargs grep -w "module_init.*)[^;]" |more
Of the results we get, the most promissing is:
./include/linux/init.h:#define module_init(initfn) \
Looking in ./include/linux/init.h
we see that module_init
is a call to __initcall
if MODULE is not defined or else
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
At this point we are really on another subject - how drivers and modules are registered. HTH.