分类: LINUX
2009-09-28 18:58:16
一. DPM 概念 Dynamic Power Management
二. Basic Concept of DPM
操作点:
/* internal representation of an operating point */
struct dpm_opt {
char *name; /* name */
struct list_head list; /* all installed op points */
dpm_md_pp_t pp[DPM_PP_NBR]; /* initialization params */
struct dpm_md_opt md_opt; /* machine dependent part */
int constrained; /* is this opt constrained? */
struct dpm_stats stats; /* statistics */
};
/* Instances of this structure define valid Innovator operating points for DPM.
Voltages are represented in mV, and frequencies are represented in KHz. */
struct dpm_md_opt {
unsigned int v; /* Target voltage in mV */
unsigned int dpll; /* in KHz */
unsigned int cpu; /* CPU frequency in KHz */
unsigned int tc; /* in KHz */
unsigned int per; /* in KHz */
unsigned int dsp; /* in KHz */
unsigned int dspmmu; /* in KHz */
unsigned int lcd; /* in KHz */
struct dpm_regs regs; /* Register values */
};
dpm操做点结构,里面包含了大约16个参数dpm_md_pp_t pp[DPM_PP_NBR];
dpm_md_pp_t是个int型
类和策略:
* internal representation of a class of op points (to be mapped to an
* operating state */
struct dpm_class {
char *name; /* name */
struct list_head list; /* all installed classes */
unsigned nops; /* nbr ops in this class */
struct dpm_opt **ops; /* the ops in this class */
struct dpm_opt *opt; /* the selected op point */
struct dpm_stats stats; /* statistics */
};
/* internal representation of an installed power policy */
struct dpm_policy {
char *name; /* name */
struct list_head list; /* all installed policies */
struct dpm_class *classes[DPM_STATES]; /* the classes */
struct dpm_stats stats; /* statistics */
};
可以看到操作点,类和策略在系统中以链的形式维护
/* curently installed policies, classes and operating points */
extern struct list_head dpm_policies;
extern struct list_head dpm_classes;
extern struct list_head dpm_opts;
三. Interfaces of DPM
3.1 User Space Interface
/* single system call to invoke all functions. the "params" argument can be
* NULL for DPM_INIT, DPM_TERMINATE, DPM_DISABLE, and DPM_ENABLE */
#if defined(__KERNEL__)
extern int sys_dpm(int func, struct dpm_param *params);
#else
#define sys_dpm(FUNC, PARAMS) \
syscall(__NR_sys_dpm, FUNC, PARAMS)
#endif
/* function names for sys_dpm system call interface */
typedef enum {
DPM_INIT, /* initialize the DPM */
DPM_TERMINATE, /* terminate the DPM */
DPM_DISABLE, /* temporarily disable the DPM */
DPM_ENABLE, /* re-enable the disabled DPM */
DPM_CREATE_OPT, /* create an operating point */
DPM_CREATE_CLASS, /* create a class of operating points */
DPM_CREATE_POLICY, /* create a policy */
DPM_DESTROY_POLICY, /* destroy a policy */
DPM_SET_POLICY, /* set the active policy */
DPM_GET_POLICY, /* get the name of the active policy */
DPM_GET_ALL_POLICIES, /* get the names of all active policies */
DPM_GET_CLASSES, /* get the names of all active policy */
DPM_SET_TASK_STATE, /* set a task-specific operating state */
DPM_GET_TASK_STATE, /* get a task's task-specific oper state */
/* get statistics */
DPM_GET_POLICY_STATS,
DPM_GET_CLASS_STATS,
DPM_GET_OPT_STATS,
DPM_GET_OS_STATS,
/* debug */
DPM_DISPLAY_POLICY,
DPM_SET_STATE, /* set a (non-scheduling-based) op state */
} dpm_func_t;
asmlinkage int sys_dpm(int func, struct dpm_param *params);
3.2 Interface for Drivers
然后看看dpm为内核和用户态应用程序提供的接口:
/*
* kernel's operating state interface
*/
/* set operating state */
EXPORT_SYMBOL(dpm_set_os);
void dpm_set_os(dpm_state_t state);
EXPORT_SYMBOL(dpm_init);
EXPORT_SYMBOL(dpm_terminate);br />EXPORT_SYMBOL(dpm_disable);
EXPORT_SYMBOL(dpm_enable);
EXPORT_SYMBOL(dpm_create_opt);
EXPORT_SYMBOL(dpm_create_class);
EXPORT_SYMBOL(dpm_create_policy);
EXPORT_SYMBOL(dpm_destroy_policy);
EXPORT_SYMBOL(dpm_set_policy);
/* ?? Needed in kernel mode ?? EXPORT_SYMBOL(dpm_get_policy);*/
EXPORT_SYMBOL(dpm_set_task_state);
EXPORT_SYMBOL(dpm_get_task_state);
/*****************************************************************************
* Suspend/Resume DPM
* The current operating point is saved and restored. This
* interface is designed to be used by system suspend/resume code, to safely
* save/restore the DPM operating point across a system power-down, where the
* firmware may resume the system at a random operating point. This does not
* require DPM to be enabled. Note that DPM remains locked across the
* suspend/resume.
*****************************************************************************/
int dpm_suspend(void)
int dpm_resume(void)
3.3 proc fs interface
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* /proc/driver/dpm/cmd (Write-Only)
*
* Writing a string to this file is equivalent to issuing a DPM command.
* Currently only one command per "write" is allowed, and there is a maximum on
* the number of tokens that will be accepted (PAGE_SIZE / sizeof(char *)).
* DPM can be initialized by a linewise copy of a configuration file to this
* /proc file.
*
* DPM Control
* -----------
*
* init : dpm_init()
* enable : dpm_enable()
* disable : dpm_disable()
* terminate : dpm_terminate()
*
* Policy Control
* --------------
*
* set_policy
* set_task_state
*
* Policy Creation
* ---------------
*
* create_opt
* Create a named operating point from DPM_PP_NBR paramaters. All
* parameters must be given. Parameter order and meaning are machine
* dependent.
*
* create_class
* Create a named class from 1 or more named operating points. All
* operating points must be defined before the call.
*
* create_policy
* Create a named policy from DPM_STATES class names. All classes must be
* defined before the call. The order is machine dependent.
*
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
四. DPM states
那么dpm的状态如何定义的呢?
machine dependent operating state
An operating state is a cpu execution state that (has implications for)(有关,牵涉) power
management. The DPM will select operating points (操作点)based largely on the
current operating state.
*
* DPM_STATES is the number of supported operating states. Valid operating
* states are from 0 to DPM_STATES-1 but when setting an operating state the
* kernel should only specify a state from the set of "base states" and should
* do so by name. During the context switch the new operating state is simply
* extracted from current->dpm_state.
*
* task states:
*
* APIs that reference task states use the range -(DPM_TASK_STATE_LIMIT + 1)
* through +DPM_TASK_STATE_LIMIT. This value is added to DPM_TASK_STATE to
* obtain the downward or upward adjusted task state value. The
* -(DPM_TASK_STATE_LIMIT + 1) value is interpreted specially, and equates to
* DPM_NO_STATE.
*
* Tasks inherit their task operating states across calls to
* fork(). DPM_TASK_STATE is the default operating state for all tasks, and is
* inherited from init. Tasks can change (or have changed) their tasks states
* using the DPM_SET_TASK_STATE variant of the sys_dpm() system call. */
#define DPM_NO_STATE -1 , 这个状态是不受电源管理的状态,有些任务根本不受dpm的托管。
以下三个是dpm的基本状态
/*states below are dpm base states, dpm基本状态*/
#define DPM_RELOCK_STATE 0 //??
#define DPM_IDLE_TASK_STATE 1
#define DPM_IDLE_STATE 2
#define DPM_SLEEP_STATE 3
#define DPM_BASE_STATES 4 , 定义了dpm的基本状态的数量,只有四种。
以下几个定义了dpm task state的状态,这些state和task相关。可以通过sys_dpm(DPM_SET_TASK_STATE)来设定。
#define DPM_TASK_STATE_LIMIT 4 ,定义了dpm的task operating state的最大数量
/*DPM_TASK_STATE is the 缺省状态 for all tasks, and is
* inherited from init. */
#define DPM_TASK_STATE (DPM_BASE_STATES + DPM_TASK_STATE_LIMIT)
/*number of supported operating states*/
#define DPM_STATES (DPM_TASK_STATE + DPM_TASK_STATE_LIMIT + 1) ,定义了整个dpm operating state的数量。
#define DPM_TASK_STATES (DPM_STATES - DPM_BASE_STATES)
#define DPM_STATE_NAMES \
{ "relock", "idle-task", "idle", "sleep",\
"task-4", "task-3", "task-2", "task-1",\
"task", \
"task+1", "task+2", "task+3", "task+4" \
}
sys_dpm() -> u_dpm_set_task_state() -> dpm_set_task_state() -> dpm_set_os()
在看一个设定task state的操作:
static int u_dpm_set_task_state(pid_t pid, dpm_state_t task_state)
{
return dpm_set_task_state(pid, task_state);
}
/*****************************************************************************
* set a task state
*****************************************************************************/
int
dpm_set_task_state(pid_t pid, dpm_state_t task_state)
{
struct task_struct *p;
/*task_state值的合法性检查*/
if (task_state == -(DPM_TASK_STATE_LIMIT + 1))
task_state = DPM_NO_STATE;
else if (abs(task_state) > DPM_TASK_STATE_LIMIT)
{
dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -EINVAL);
return -EINVAL;
}
else
{
/*task_state值得调整,DPM_TASK_STATE是一个默认值
设定的值比默认值或大或小,这样相当于加了
个基准值而已-(DPM_TASK_STATE_LIMIT + 1) < task_state <= DPM_TASK_STATE_LIMIT*/
task_state += DPM_TASK_STATE;
}
read_lock(&tasklist_lock);
if (pid == 0)
p = current;
else
p = find_task_by_pid(pid);
if (!p) {
read_unlock(&tasklist_lock);
dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, -ENOENT);
return -ENOENT;
}
p->dpm_state = task_state;
read_unlock(&tasklist_lock);
dpm_trace(DPM_TRACE_SET_TASK_STATE, pid, task_state, 0);
/*如果是当前进程,马上切换状态*/
if (pid == 0)
dpm_set_os(p->dpm_state);
return 0;
}
以下的低层调用 dpm_set_os() -> dpm_resync() -> dpm_set_opt_async() -> dpm_md_set_opt() -> dpm_omap24xx_set_opt()
dpm_omap24xx_set_opt() 是一个与体系结构相关的set opt函数,在初始化的时候被注册。下面还会分析。
以下更低层的的调用最终会调用到omap24xx_fscaler(),定义在
这个函数做实际的事情,根据设定好的操作点设定cpu和相关硬件的状态,完成了dpm状态的切换。比如:
//= POWER MODES =//
#define PRCM_OFF 0
#define PRCM_ON 1
#define PRCM_RETENTION 2
#define PRCM_SLEEP 3
#define PRCM_STANDBY 4
#define PRCM_DORMANT 5
static void omap24xx_fscaler(struct dpm_regs *regs) -> omap24xx_pm_suspend(PRCM_STANDBY) ...
Just a piece of code:
if(dpm_fscaler_flags & DPM_FSCALER_ANY_SLEEPMODE) {
/* omap24xx_pm_suspend() handles voltage scaling */
DPRINT("\nsuspending devices\n\n");
device_suspend(0, SUSPEND_POWER_DOWN);
if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_DEVICEON) {
omap24xx_pm_suspend(PRCM_ON);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_STANDBY){
omap24xx_pm_suspend(PRCM_STANDBY);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_SLEEP){
omap24xx_pm_suspend(PRCM_SLEEP);
}
else if(dpm_fscaler_flags & DPM_FSCALER_SLEEP_RETENTION){
omap24xx_pm_suspend(PRCM_RETENTION);
}
/* Here when we wake up. */
/* Power on devices again */
device_resume(RESUME_POWER_ON);
五. Kernel supported on DPM
5.1 task_struct
在task_struct中,添加了对dpm的支持:
/* dynamic power management */
int dpm_state;
这个状态就设定为上述提到的dpm task struct task状态之一。用dpm_set_task_state()来进行状态的切换。
子进程继承父进程的dpm状态,
另外,在支持dpm的kernel中,有许多类似的结构都添加了对dpm的支持。例如:
struct device_driver {
char * name;
struct bus_type * bus;
struct device_class * devclass;
struct semaphore unload_sem;
struct kobject kobj;
struct list_head bus_list;
struct list_head class_list;
struct list_head devices;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
#if 1 /* linux-pm */
int (*scale) (struct bus_op_point * op, u32 level);
#endif /* linux-pm */
};
整个dpm架构在Linux kernel中以device driver的形式存在,提供了支持应用程序和内核及其他driver的接口。
六. DPM Initializing
了解了这些知识,看看dpm的初始化:
一看这个目录就知道了这个可能是个driver,至少是个内核模块,至于怎么编译就不管了,可以静态编译到内核中.所以你肯定会去找module_init拉.
#ifdef MODULE
module_init(dpm_init_module);
module_exit(dpm_exit_module);
#else
__initcall(dpm_init_module);
#endif
看见了初始化函数是dpm_init_module:
int dpm_init_module(void)
{
#ifndef CONFIG_MACH_OMAP1610_STBPM
/* ensure syscall slot is available */
if (sys_call_table[sys_dpm_nr] != sys_call_table[0])
return 1;
/* install our syscall */
sys_call_table[sys_dpm_nr] = sys_dpm;
#ifdef CONFIG_PROC_FS
{
void dpm_proc_init(void);
dpm_proc_init();
}
#endif
dpm_md_init();
trace("DPM is now installed\n");
#endif /* CONFIG_MACH_OMAP1610_STBPM */
return 0;
}
(1) 注册自己的系统调用 sys_dpm,作为用户态应用程序的接口
(2) 调用dpm_md_init()完成自己的初始化.
static inline void dpm_md_init(void)
{
if (dpm_md.init)
dpm_md.init();
}
int __init dpm_omap24xx_init(void)
{
printk("OMAP24XX Dynamic Power Management\n");
/*在这个地方连接cpu相关的初始化函数*/
dpm_md.init = NULL;
dpm_md.init_opt = dpm_omap24xx_init_opt;
dpm_md.set_opt = dpm_omap24xx_set_opt;
dpm_md.get_opt = dpm_omap24xx_get_opt;
dpm_md.validate_opt = dpm_omap24xx_validate_opt;
dpm_md.idle_set_parms = NULL;
dpm_md.cleanup = dpm_omap24xx_cleanup;
#ifdef MAYBE
/* +++ necessary? */
dpm_omap24xx_board_setup();
#endif
#ifdef LATER
dpm_bd.init();
#endif
omap24xx_clk_init();
omap24xx_vcs_init();
return 0;
}
七. DPM idle and DPM suspend
说一下idle:
int __init
dpm_omap_init(void)
{
printk("TI OMAP Dynamic Power Management.\n");
dpm_md.init_opt = dpm_omap_init_opt;
dpm_md.set_opt = dpm_omap_set_opt;
dpm_md.get_opt = dpm_omap_get_opt;
dpm_md.validate_opt = dpm_omap_validate_opt;
dpm_md.idle_set_parms = NULL;
dpm_md.cleanup = dpm_omap_cleanup;
return 0;
}
__initcall(dpm_omap_init);
发现了对硬件的实际操作基本都在
static int __init omap_pm_init(void)
{
unsigned char addr, data;
u32 yy;
u32 zz;
printk("Power Management for TI OMAP.\n");
pm_idle = dpm_idle;
memcpy((void *)ISRAM_PROGRAM, omap2420_cpu_suspend,
omap2420_cpu_suspend_sz);
addr = 0x55; /* IOSTATE2 */
Spi1_read(SPI1_CS1, 1, &addr, &data);
if((data & 0x04) == 0x04){ /* BRB? */
pm_BRB_mode = 1;
}
else {
pm_BRB_mode = 0;
}
/* GPIO wakeup settings */
__raw_writel(0x0002A000, OMAP2420_GPIO1_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO1_SETWKUENA */
__raw_writel(0x0000000C, OMAP2420_GPIO2_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO2_SETWKUENA */
__raw_writel(0x00004010, OMAP2420_GPIO3_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO3_SETWKUENA */
__raw_writel(0x00100020, OMAP2420_GPIO4_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO4_SETWKUENA */
__raw_writel(0x00000000, OMAP2420_GPIO5_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO5_SETWKUENA */
#ifdef CONFIG_ICE_PATCH_PM
if(pm_BRB_mode)
{
request_irq(INT_GPIO_121,gpio121_dummy_handler,SA_INTERRUPT,"gpio121_dummy",NULL);
__REG32(0x49012034) |= (1<<25) ;
__REG32(0x49012040) &= ~(1<<25);
__REG32(0x49012044) |= (1<<25) ;
__REG32(0x4901201C) |= (1<<25) ;
__REG32(0x49012048) |= (1<<25) ;
__raw_writel(0x02100020, OMAP2420_GPIO4_BASE + OMAP2420_GPIO_SET_WKUENA);/* GPIO4_SETWKUENA */
}
#endif
if(stm_dev_sts[DRV_UART1] == STAT_OFF) {
/* disable UART1 function clock */
CM_FCLKEN1_CORE &= ~(1<<21); /* CM_FCLKEN1_CORE:EN_UART1 */
CM_ICLKEN1_CORE &= ~(1<<21); /* CM_ICLKEN1_CORE:EN_UART1 */
}
yy = __REG32(0x6D000060) & 0x0000FF00; /* SDRC_DLLA_CTRL(0x6D000060) 0x0000yy00 */
zz = __REG32(0x6D000068) & 0x0000FF00; /* SDRC_DLLB_CTRL(0x6D000068) 0x0000zz00 */
*((u32 *)(ISRAM_PROGRAM + omap2420_val_SDRC_DLLA_CTRL_yy_offset)) = yy;
*((u32 *)(ISRAM_PROGRAM + omap2420_val_SDRC_DLLB_CTRL_zz_offset)) = zz;
pm_loops_per_jiffy = loops_per_jiffy;
return 0;
}
__initcall(omap_pm_init);
可以看到很多都是对硬件设备的直接操作。
那么既然这里把pm_idle初始化成了dpm_idle(),所以dpm_idle就是实际的dpm idle state的处理函数。
我们看在linux中何时会处理dpm idle state,也就是说硬件驱动层和内核的接口。
void (*pm_power_off)(void);
void (*pm_idle)(void);
EXPORT_SYMBOL(pm_idle); pm_idle也会被kernel export出来,做为驱动程序的接口
/*
* The idle thread. We try to conserve power, while trying to keep
* overall latency low. The architecture specific idle is passed
* a value to indicate the level of "idleness" of the system.
*/
void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
while (!need_resched()) {
void (*idle)(void) = pm_idle;
if (!idle)
idle = default_idle;
leds_event(led_idle_start);
dpm_set_os(DPM_IDLE_STATE);
idle();
dpm_set_os(DPM_IDLE_TASK_STATE);
leds_event(led_idle_end);
}
schedule();
#ifndef CONFIG_NO_PGT_CACHE
check_pgt_cache();
#endif
}
}
最关键的几句:
dpm_set_os(DPM_IDLE_STATE);
idle(); (由上述分析,实际会调用dpm_idle())
dpm_set_os(DPM_IDLE_TASK_STATE);
看看dpm_idle里面到底做了什么:
/* If DPM is currently disabled here we simply do the standard
idle wait.
If we're not actually in DPM_IDLE_TASK_STATE, we need to go back and get
into this state. This could happen in rare instances - an interrupt between
dpm_set_os() and the critical section.
If we are not yet at the idle-task operating point, or if there is no
difference between idle-task and idle, we can enter/exit the idle state
quickly since it's only for statistical purposes. This is also true if for
some reason we can't get the DPM lock, since obviously an asynchronous event
is going to have to occur to clear the lock, and this event is going to take
us out of idle.
Otherwise the full idle shutdown is done. */
dpm_idle() -> full_idle()-> omap24xx_pm_idle()
这个是NEC dpm中做实际事情的最内层的调用函数:
void omap24xx_pm_idle(void)
{
TRACE_PROCESS(TRACE_EV_PROCESS_IDLE_BEGIN, 0, 0);
if(dpm_enabled) {
if(status_idle_judge_func() == STM_IDLE) {
device_suspend(0, SUSPEND_POWER_DOWN);
local_irq_disable();
local_fiq_disable();
if( OMAP_SLEEP_ENABLE == do_sleep_setup() ) {
int dummy = PRCM_SLEEP;
omap24xx_pm_suspend(dummy);
}
local_fiq_enable();
local_irq_enable();
device_resume(RESUME_POWER_ON);
}
}
}
可以看到做的基本的事情就是:
device_suspend(0, SUSPEND_POWER_DOWN);
omap24xx_pm_suspend(dummy);
device_resume(RESUME_POWER_ON);
实际做事情的函数是
omap24xx_pm_suspend(PRCM_SLEEP)
里面有一句:
func_ptr = (void *)(ISRAM_PROGRAM);
ISRAM_PROGRAM是一个IO space的地址。
在上面提到的初始化的时候有这么一句:
memcpy((void *)ISRAM_PROGRAM, omap2420_cpu_suspend,
omap2420_cpu_suspend_sz);
#define ISRAM_PROGRAM IO_ADDRESS(0x40200000)
所以这里实际会执行omap2420_cpu_suspend,另外其余部分还完成了许多对硬件的操作。
现在回到
这个是个大范围的suspend,涉及到所有已经注册device.
int device_suspend(u32 state, u32 level)
{
struct list_head * node;
int error = 0;
#if 0 /* linux-pm */
printk(KERN_EMERG "Suspending devices\n");
#endif /* linux-pm */
down(&device_sem);
list_for_each_prev(node,&global_device_list) {
struct device * dev = to_dev(node);
#ifdef CONFIG_SERIAL_CONSOLE
if (dev == sercons_dev)
continue;
#endif
#ifdef CONFIG_I2C_OMAP24XX
if ((dev == i2c_device[0]) || (dev == i2c_device[1]))
continue;
#endif
if (dev->driver && dev->driver->suspend) {
pr_debug("suspending device %s\n",dev->name);
error = dev->driver->suspend(dev,state,level);
if (error)
printk(KERN_ERR "%s: suspend returned %d\n",dev->name,error);
}
}
#ifdef CONFIG_SERIAL_CONSOLE
if (sercons_dev)
sercons_dev->driver->suspend(sercons_dev,state,level);
#endif
#ifdef CONFIG_I2C_OMAP24XX
if (i2c_device[0])
i2c_device[0]->driver->suspend(i2c_device[0],state,level);
if (i2c_device[1])
i2c_device[1]->driver->suspend(i2c_device[1],state,level);
#endif
up(&device_sem);
return error;
}
意思不用过多阐述了. 不明白的看第八部分.
八. Linux Device Module
LIST_HEAD(global_device_list);
每个device都是这样的一个结构:
struct device {
struct list_head g_list; /* node in depth-first order list */
struct list_head node; /* node in sibling list */
struct list_head bus_list; /* node in bus's list */
struct list_head driver_list;
struct list_head children;
structlist_head intf_list;
struct device * parent;
struct kobject kobj;
char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct bus_type * bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *driver_data; /* data private to the driver */
u32 class_num; /* class-enumerated value */
void * class_data; /* class-specific data */
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
u32 power_state; /* Current operating state. In
ACPI-speak, this is D0-D3, D0
being fully functional, and D3
being off. */
#if 1 /* linux-pm */
struct constraints *constraints;
#endif /* linux-pm */
unsigned char *saved_state; /* saved device state */
void (*release)(struct device * dev);
};
#define to_dev(obj) container_of(obj,struct device,kobj)
#define KOBJ_NAME_LEN 16
struct kobject {
char name[KOBJ_NAME_LEN];
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
struct subsystem * subsys;
struct dentry * dentry;
};
/**
* get_device - increment reference count for device.
* @dev: device.
*
* This simply forwards the call to kobject_get(), though
* we do take care to provide for the case that we get a NULL
* pointer passed in.
*/
struct device * get_device(struct device * dev)
{
return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
}
to_dev() 将kobject_get()加引用计数后的kobj结构还原(找到上层"wrapper")到dev结构
#define to_dev(obj) container_of(obj,struct device,kobj)
/**
* container_of - cast a member of a structure out to the containing structure
*
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
上述内容属于Linux Driver Moudule的东西,只是Linux内核提供给驱动程序的接口,是一层wrapper,或者说屏蔽了底层硬件的差异,提供了一个统一的逻辑接口。
相关的操作有:
EXPORT_SYMBOL(device_initialize);
EXPORT_SYMBOL(device_add);
EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_del);
EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device);
EXPORT_SYMBOL(put_device);
EXPORT_SYMBOL(device_create_file);
EXPORT_SYMBOL(device_remove_file);
你可以从
我们在device结构中可以看到这样的一个结构
struct device_driver *driver; /* which driver has allocated this device */
这就是一个driver的结构, 什么意思, 比如你的系统里面有pci,i2c等总线,这些总线上可能会挂上一个或者多个同一标准接口的device,所以就把这个总线的公共操作能够抽象出来,也就可以看成是总线的driver.这个结构定义在
其实也可以这样想,每个device如果关联一个driver结构的话,肯定会比较灵活.因为即便是一个pci总线,也可以对不同的device有不同的driver,这样就提供了一个中间wrapper层,理论上是总线控制设备,但是实际实现的时候操作会经过这个中间层,比如一个特定的device最终会找到它对应的driver,实现它特有的操作.这就灵活了.
上面这段文字实际上是给你们把这个架构举了一个例子,实际上这个例子只是给用户便于理解.其实suspend和resume,shutdown诸如此类的命令并不是总线直接发出的,真正的主宰还是用户的驱动程序,它们通过调用Linux Device Module这一层提供的公共接口去控制其他注册在系统中的设备,或者控制它自己.
struct device_driver {
char * name;
struct bus_type * bus;
struct device_class * devclass;
struct semaphore unload_sem;
struct kobject kobj;
struct list_head bus_list;
struct list_head class_list;
struct list_head devices;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
int (*suspend) (struct device * dev, u32 state, u32 level);
int (*resume) (struct device * dev, u32 level);
#if 1 /* linux-pm */
int (*scale) (struct bus_op_point * op, u32 level);
#endif /* linux-pm */
};
shutdown不说了,suspend和resume是挂起和恢复的操作,比如进入省电模式时这些设备可能会suspend以节省功耗,恢复时肯定是用resume. remove是移出一个设备. probe的意思是探测,负责完成对硬件的检测工作, 在适当的时刻会被调用,比如设备初始化或者打开设备的时候.
很显然,driver肯定也有一套接口给出,给其他的驱动程序调用.
EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(driver_create_file);
EXPORT_SYMBOL(driver_remove_file);
在
举个例子:
void omap2_bus_register(struct device *device,
struct device_driver *driver)
{
if(!device || !driver)
return ;
if(!driver->suspend || !driver->resume)
return ;
driver->bus = &omap2_private_bus ;
(void) driver_register(driver) ;
device->parent = &omap2_private_device ;
device->bus = &omap2_private_bus ;
device_register(device) ;
return ;
}