br
有效
大於符號 > . > 小魚 < . < 一起 < > < >code:
大於 > . >
小於 < . < 一起 < > > >
縮
排
也
option
[1]1
[2] 2
1!
3
ads
sss
kkk
a b c
ubuntu 在 R40e 上 還有 Debian 在 Sempron 2600 上
Power manager options
[*] Power Management Debug Support
[*] Verbose Power Management debugging
[*] Suspend to RAM and standby
[*] Test suspend/resume and walealarm during bootup
上面幾個 option 打開。這樣 config 就會有
CONFIG_PM_DEBUG=y
CONFIG_PM_VREBOSE=y
CONFIG_PM_TEST_SUSPEND=y
這樣,在 kernel/power 下
main.c:
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
#endif
#ifdef CONFIG_SUSPEND_DEVICE_TIME_DEBUG
&device_suspend_time_threshold_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_USER_WAKELOCK
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#endif
NULL,
};
可以看到每個 option 增加的 entry.
CONFIG_PM_DEBUG,就會有 pm_test_attr 這個 entry
宣告的 pm_test_attr 有:
#ifdef CONFIG_PM_DEBUG
int pm_test_level = TEST_NONE;
static const char * const pm_tests[__TEST_AFTER_LAST] = {
[TEST_NONE] = "none",
[TEST_CORE] = "core",
[TEST_CPUS] = "processors",
[TEST_PLATFORM] = "platform",
[TEST_DEVICES] = "devices",
[TEST_FREEZER] = "freezer",
};
static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
int level;
for (level = TEST_FIRST; level <= TEST_MAX; level++)
if (pm_tests[level]) {
if (level == pm_test_level)
s += sprintf(s, "[%s] ", pm_tests[level]);
else
s += sprintf(s, "%s ", pm_tests[level]);
}
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
return (s - buf);
}
static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
const char * const *s;
int level;
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
mutex_lock(&pm_mutex);
level = TEST_FIRST;
for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
pm_test_level = level;
error = 0;
break;
}
mutex_unlock(&pm_mutex);
return error ? error : n;
}
power_attr(pm_test);
#endif /* CONFIG_PM_DEBUG */
所以在 /sys/power 下會多一個 pm_test。 cat 出來會是
none core processors platform devices freezser
這個功能是用來測試 suspend/resume 的。
從 source code : kernel/power/suspend.c
static int suspend_enter(suspend_state_t state)
{
.....
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS))
goto Enable_cpus;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error = sysdev_suspend(PMSG_SUSPEND);
if (!error) {
if (!suspend_test(TEST_CORE))
error = suspend_ops->enter(state);
sysdev_resume();
}
...
和 suspend_test( ) function :
static int suspend_test(int level)
{
#ifdef CONFIG_PM_DEBUG
if (pm_test_level == level) {
printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");
mdelay(5000);
return 1;
}
#endif /* !CONFIG_PM_DEBUG */
return 0;
}
可以看到, core, processors, platform, devices, freezer 各是不同的 suspend level。#echo core > /sys/power/pm_test然後再 trigger suspend...
就會看到 suspend 後 5 sec 就自己 wakeup 了
git log --grep="suspend"
找到 arch 裡的 pm.c suspend_set_ops
static struct platform_suspend_ops *suspend_ops;
void suspend_set_ops(struct platform_suspend_ops *ops)
{
mutex_lock(&pm_mutex);
suspend_ops = ops;
mutex_unlock(&pm_mutex);
}
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*prepare_late)(void);
int (*enter)(suspend_state_t state);
void (*wake)(void);
void (*finish)(void);
void (*end)(void);
void (*recover)(void);
};
extern int set_irq_wake(unsigned int irq, unsigned int on);
static inline int enable_irq_wake(unsigned int irq)
{
return set_irq_wake(irq, 1);
}
static inline int disable_irq_wake(unsigned int irq)
{
return set_irq_wake(irq, 0);
}
在 kernel/irq/manager.c:
/**
* set_irq_wake - control irq power management wakeup
* @irq: interrupt to control
* @on: enable/disable power management wakeup
*
* Enable/disable power management wakeup mode, which is
* disabled by default. Enables and disables must match,
* just as they match for non-wakeup mode support.
*
* Wakeup mode lets this IRQ wake the system from sleep
* states like "suspend to RAM".
*/
int set_irq_wake(unsigned int irq, unsigned int on)
{
struct irq_desc *desc = irq_to_desc(irq);
unsigned long flags;
int ret = 0;
/* wakeup-capable irqs can be shared between drivers that
* don't need to have the same sleep mode behaviors.
*/
raw_spin_lock_irqsave(&desc->lock, flags);
if (on) {
if (desc->wake_depth++ == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 0;
else
desc->status |= IRQ_WAKEUP;
}
} else {
if (desc->wake_depth == 0) {
WARN(1, "Unbalanced IRQ %d wake disable\n", irq);
} else if (--desc->wake_depth == 0) {
ret = set_irq_wake_real(irq, on);
if (ret)
desc->wake_depth = 1;
else
desc->status &= ~IRQ_WAKEUP;
}
}
raw_spin_unlock_irqrestore(&desc->lock, flags);
return ret;
}
那個irq_to_desc,在 kernel/irq/handle.c.
struct irq_desc *irq_to_desc(unsigned int irq)
{
return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}
那個 irq_desc 是宣告成:
struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
[0 ... NR_IRQS-1] = {
.status = IRQ_DISABLED,
.chip = &no_irq_chip,
.handle_irq = handle_bad_irq,
.depth = 1,
.lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
}
};
這個 irq_struct 宣告在 include/linux/irq.h
/**
* struct irq_desc - interrupt descriptor
* @irq: interrupt number for this descriptor
* @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @irq_2_iommu: iommu with this irq
* @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
* @chip: low level interrupt hardware access
* @msi_desc: MSI descriptor
* @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
* @action: the irq action chain
* @status: status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity: IRQ affinity on SMP
* @node: node index useful for balancing
* @pending_mask: pending rebalanced interrupts
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
unsigned int irq;
struct timer_rand_state *timer_rand_state;
unsigned int *kstat_irqs;
#ifdef CONFIG_INTR_REMAP
struct irq_2_iommu *irq_2_iommu;
#endif
irq_flow_handler_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_var_t affinity;
const struct cpumask *affinity_hint;
unsigned int node;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;
WPA_SUPPLICANT_VERSION := VER_0_6_ATHEROS
然後在 external 下有三個 wpa_supplicant :
ifeq ($(WPA_SUPPLICANT_VERSION),VER_0_5_X)
這樣決定int wifiApState = mWifiManager.getWifiApState();
...
if (mWifiManager.setWifiEnabled(enable)) {
其中的 mWifiManager 是 system service wrapper class:
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(enabled);
} catch (RemoteException e) {
return false;
}
}
mService 是 IBinder interface :
public WifiManager(IWifiManager service, Handler handler) {
mService = service;
mHandler = handler;
}
IWifiManager 是 (Binder interface): IWifiManager.aidlpublic class WifiService extends IWifiManager.Stub {
sendEnableMessage(enable, true, Binder.getCallingUid());
sendEnableMessage()
private void sendEnableMessage(boolean enable, boolean persist, int uid) {
Message msg = Message.obtain(mWifiHandler,
(enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
(persist ? 1 : 0), uid);
msg.sendToTarget();
}
這個 message 的 hanelder:
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_ENABLE_WIFI:
setWifiEnabledBlocking(true, msg.arg1 == 1, msg.arg2);
所以是 setWifiEnabledBlocking : 這個做了 wifi driver loading, connect to wpa.. etc
private boolean setWifiEnabledBlocking(boolean enable, boolean persist, int uid) {
....
if (enable) {
if (!mWifiStateTracker.loadDriver()) {
Slog.e(TAG, "Failed to load Wi-Fi driver.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
}
if (!mWifiStateTracker.startSupplicant()) {
mWifiStateTracker.unloadDriver();
Slog.e(TAG, "Failed to start supplicant daemon.");
setWifiEnabledState(WIFI_STATE_UNKNOWN, uid);
return false;
WifiStateTracker 在 framework/base/wifi ...
public synchronized boolean loadDriver() {
return WifiNative.loadDriver();
}
call 到 jni : framework/base/core/jni/android_net_wifi_Wifi.cpp ,這只是 call 到 c code 的 wrapper。 if (insmod(DRIVER_MODULE_PATH, DRIVER_MODULE_ARG) < 0)
driver name, path, argument 都是寫在 Android.mk 裡:
WIFI_DRIVER_MODULE_PATH := "/system/lib/modules/ar6000.ko"
WIFI_DRIVER_MODULE_ARG := ""
WIFI_DRIVER_MODULE_NAME := "ar6000"