分类: LINUX
2013-09-07 17:05:21
**********************************************设备驱动架构的分层与分离思想**********************************************************/
/*
*设备驱动的分层思想:在设备驱动方便,往往为同类的设备设计一个框架,而在框架中的核心层则实现了该设备驱动通用的一些功能,同样的,如果在具体的
*设备驱动中不想使用或者由于硬件设计的关系不使用核心层的函数,它可以重载之,例子如下:
*/
return_type core_func(xxx_device *xxx_dev,param1,param2)
{
if(xxx_dev->funca) //判断底层设备是否重载了funca(底层是否定义了该函数),如果重载了(底层定义了该函数),就调用底层提供的funca函数,否则直接使用通用层的代码
return xxx_dev->funca(.......);
/* 核心层通用的funca代码 */
.......
}
/*
*主机驱动与外设驱动分离思想:
* 该思想是将主机控制器驱动和外设驱动进行分离,外设的驱动与主机控制器的驱动不相关,主机控制器的驱动不关心外设,而外设驱动不关心主机控制器。
*这样做了以后,那么要将它们联系起来就需要设计一个核心层,外设只是通过核心层的通用API进行数据传输(核心层通常需要定义主机和设备使用的数据结构、
*将主机控制器和设备联系起来的代码,用于在两者之间传输数据的代码等等)。linux的SPI、I2C、USB、ASoC等子系统都典型地利用了这种分离的设计思想
*/
/**********************************************************end***********************************************************************/
/****************************************************platform设备驱动****************************************************************/
/*
* 在2.6内核的设备驱动模型中,总是关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动,在系统每注册一个驱动的时候,
*会寻找与之相匹配的设备,而匹配由总线完成。
*
* linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动称为platform_driver,所谓的platform_device并不是与字符设备、块设备和
*网络设备并列的概念,而是linux系统提供的一种附加手段
*
*
*相关结构和接口函数在platform_device.h、ioport.h、platform.c
*/
/*=====================================================平台设备相关的数据结构=============================================================*/
#include
-----------------------------------struct platform_device--------------------------------------
platform_device结构体定义如下:
struct platform_device { //表示挂在platform总线的设备
const char * name; //设备名,一定要与struct platform_driver->struct device_driver->name相同
int id; //支持的设备,通常取-1支持所有匹配的设备
struct device dev;
u32 num_resources; //设备所使用的各类资源数量
struct resource * resource; //资源
struct platform_device_id *id_entry;
};
-----------------------------------------------------------------------------------------------
/*---------------------------------------struct device-------------------------------------------*/
//device结构:
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
struct device_type *type;
struct semaphore sem; /* semaphore to synchronize calls to
* its driver.
*/
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 */
void *platform_data; /* Platform specific data, device //可用于存放自己定义的与开发板相关的设备资源数据platform_data
core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
};
-----------------------------------------------------------------------------------------------
/*---------------------------------------struct resource-----------------------------------------*/
//resource(资源)结构:
struct resource { //表示设备使用的资源
resource_size_t start; //资源的开始值
resource_size_t end; //资源的结束值
const char *name; //
unsigned long flags; //资源类型,在linux目录下ioport.h中定义,以IORESOURCE_开头,start、end会随着flags变化而变更
struct resource *parent, *sibling, *child;
};
//对resource的定义通常在BSP的板文件中实现,而在具体的设备中通过下面这个函数来获取资源:
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num); //type=flags,num为资源引索(从0开始,其最大值为同类资源的个数-1)
int platform_get_irq(struct platform_device *dev, unsigned int num); //获取中断号资源
----------------------------------------------------------------------------------------------
/*
*设备的硬件描述除了中断、内存等以外,可能还会有一些配置信息,这些配置信息也依赖于板所以不适合直接放置在设备驱动本身,因此,platform也提供了platform_data的支持, platform_data
*的形式是自己定义的,如DM9000的platform_data为一个dm9000_plat_data结构体,我们就可以将MAC地址,总线宽度等放到自己定义的platform_data_xxx中,然后保存到struct platform_device-> struct device dev.platform_data中
*,使用的时候可以这样: struct platform_device *pdev=函数传进来的struct platform_device指针; struct platform_data_xxx *pdatax=pdev->dev.platform_data
*
*对platform_device的定义通常在BSP的板文件中实现,在板文件中,将platform_device归纳为一个数组,最终通过platform_add_devices()函数统一注册,platform_add_devices()函数可以
*将平台设备添加到系统中,这个函数的原型为:
*/
int platform_add_devices(struct platform_device **devs, int num); //devs为平台设备数组指针,num为平台设备数目,它的内部调用platform_device_register()函数注册单个的平台设备
int platform_device_register(struct platform_device *dev); //注册platform_device
void platform_device_unregister(struct platform_device *dev); //卸载platform_device
=================================================================================================================================================
/*=========================================================平台驱动相关数据结构=================================================================*/
----------------------------------struct platform_driver--------------------------------------
//platform_driver结构体包含probe()、remove()、shutdown()、suspend()、resume()函数,通常需要由驱动实现:
struct platform_driver { //挂在platform总线上的设备的对应的驱动
int (*probe)(struct platform_device *); //具体的硬件相关的操作,注册等等具体的操作都可以在这个函数里面实现
int (*remove)(struct platform_device *); //与上面相反的操作
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state); //挂起函数,用于电源管理
int (*suspend_late)(struct platform_device *, pm_message_t state); //也是挂起函数,不过它与suspend的区别是工作于中断都被禁止的情况下,而且仅有一个CPU是活跃的,绝大部分驱动不实现该函数
int (*resume_early)(struct platform_device *); //该函数也是恢复函数,它与resume区别是工作于中断都被禁止的情况下,绝大部分驱动不实现该函数
int (*resume)(struct platform_device *); //恢复函数,用于电源管理
struct device_driver driver; //driver->name一定要与与platform_device结构中设备名platform_device->name相同
struct platform_device_id *id_table;
};
---------------------------------------------------------------------------------------------
/*----------------------------------struct device_driver-------------------------------------*/
struct device_driver {
const char *name; //设备名,一定要与platform_device结构中设备名platform_device->name匹配
struct bus_type *bus;
struct module *owner; //THIS_MODULE
const char *mod_name; /* used for built-in modules */
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;
};
--------------------------------------------------------------------------------------------
//通常在suspend函数里会停止设备,并关闭给它提供的时钟,所以在suspend函数里经常看到下面的语句:
clk_disable(xxx->clk);
//而在resume函数中进行相反的操作:
clk_enable();
struct clk *clk_get(struct device *dev, const char *id); //获取时钟
void clk_put(struct clk *clk); //释放时钟
int clk_enable(struct clk *clk); //使能时钟
int clk_disable(struct clk *clk); //禁止时钟
unsigned long clk_get_rate(struct clk *clk); //获取时钟频率
long clk_round_rate(struct clk *clk,unsigned long rate); //试探时钟频率
int clk_set_rate(struct clk *clk,unsigned long rate); //设置时钟频率
//device_driver结构体:
#include
//系统中为platform总线定义了一个bus_type的实例platform_bus_type:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //该成员函数确定了platform_device和platform_driver之间如何匹配,通过比较name字段是否相同,如果相同则驱动的probe函数来进一步进行设备的具体的初始化和注册
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
int platform_driver_register(struct platform_driver *drv); //注册platform_driver
void platform_driver_unregister(struct platform_driver *drv); //卸载platform_driver
//一般的模板:
static struct resource xxx_resource[] = {
[0] = {
.start = xxxx,
.end = xxxx,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = xxx,
.end = xxx,
.flags = IORESOURCE_IRQ,
},
............
............
};
static struct platform_device xxx_device={
.name = "xxx",
.id = -1,
.num_resources = ARRAY_SIZE(xxx_resource),
.resource = xxx_resource,
.dev = {
.release = xxx_release,
},
};
static int xxx_init(void)
{
platform_device_register(&xxx_device);
return 0;
}
static void xxx_exit(void)
{
platform_device_unregister(&xxx_device);
}
module_init(xxx_init);
module_exit(xxx_exit);
以上是device:
//下面是具体的设备驱动程序实现:
static struct platform_driver xxx_device_driver={
.probe=xxx_probe,
.remove=__devexit_p(xxx_remove),
.....
.driver={
.name="xxx",
.owner=THIS_MODULE,
.......
}
};
static int __devinit xxx_probe(struct platform_device *pdev)
{
申请设备号;
为设备设备描述结构体分配内存;
注册设备;
。。。。。
}
static int __devexit xxx_remove(struct platform_device *pdev)
{
卸载设备号;
为设备设备描述结构体释放内存;
卸载设备;
。。。。。
}
static int xxx_init(void)
{
platform_driver_registe(&xxx_device_driver);
return 0;
}
static void xxx_exit(void)
{
platform_driver_unregister(&xxx_device_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
//备注:
1:在devs.c中定义了各种平台的platform_device和资源,可在该文件下添加相应的设备和资源
2:在arch/arm/mach-2410/mach-smdk2410.c中的 static struct platform_device *smdk2410_devices[] __initdata = {
&xxx_device,
};
//以完成设备的注册。这样的platform_device_register(&xxx_device)和platform_device_unregister(&xxx_device)就可以省略不用自己做了
/****************************************************end**********************************************************************/