您的位置:首页 > 运维架构 > Linux

Linux Platform驱动程序框架解析

2011-10-02 01:52 381 查看


1.platform总线、设备与驱动

在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。

2.编写platform驱动程序步骤

一、注册设备

1.为设备编写platform_device设备结构

struct platform_device {

const char * name; //设备名

int id; //设备编号

struct device dev;

u32 num_resources; //设备使用资源的数目

struct resource * resource; //设备使用资源

};

struct resource {

resource_size_t start; //资源起始地址

resource_size_t end; //资源结束地址

const char *name;

unsigned long flags; //资源类型

struct resource *parent, *sibling, *child;

};

注:struct resource结构中我们通常关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。start、end的含义会随着flags而变更,如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资源。

2.注册platform_device设备结构体

int platform_device_register(struct platform_device *pdev); //注册一个设备

int platform_add_devices(struct platform_device **pdevs, int ndev); //注册多个设备

platform设备注册例子:linux2.6.26.8内核中NAND FLASH设备注册实例:

linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c

nand控制器资源:

static struct resource s3c_nand_resource[] = {

[0] = {

.start = S3C24XX_PA_NAND,

.end = S3C24XX_PA_NAND + S3C24XX_SZ_NAND - 1,

.flags = IORESOURCE_MEM,

}

};

struct platform_device s3c_device_nand = {

.name = "s3c2410-nand",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_nand_resource),

.resource = s3c_nand_resource,

};

注册nand flash作为platform device:

linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:

static struct mtd_partition smdk_default_nand_part[] = {

[0] = {

.name = "Boot Agent",

.size = SZ_16K,

.offset = 0,

},

[1] = {

.name = "S3C2410 flash partition 1",

.offset = 0,

.size = SZ_2M,

},

[2] = {

.name = "S3C2410 flash partition 2",

.offset = SZ_4M,

.size = SZ_4M,

},

[3] = {

.name = "S3C2410 flash partition 3",

.offset = SZ_8M,

.size = SZ_2M,

},

[4] = {

.name = "S3C2410 flash partition 4",

.offset = SZ_1M * 10,

.size = SZ_4M,

},

[5] = {

.name = "S3C2410 flash partition 5",

.offset = SZ_1M * 14,

.size = SZ_1M * 10,

},

[6] = {

.name = "S3C2410 flash partition 6",

.offset = SZ_1M * 24,

.size = SZ_1M * 24,

},

[7] = {

.name = "S3C2410 flash partition 7",

.offset = SZ_1M * 48,

.size = SZ_16M,

}

};

static struct s3c2410_nand_set smdk_nand_sets[] = {

[0] = {

.name = "NAND",

.nr_chips = 1,

.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),

.partitions = smdk_default_nand_part,

},

};

static struct s3c2410_platform_nand smdk_nand_info = {

.tacls = 20,

.twrph0 = 60,

.twrph1 = 20,

.nr_sets = ARRAY_SIZE(smdk_nand_sets),

.sets = smdk_nand_sets,

};

static struct platform_device __initdata *smdk_devs[] = {

...

&s3c_device_nand,

...

};

void __init smdk_machine_init(void)

{

...

s3c_device_nand.dev.platform_data = &smdk_nand_info; //注意这里的赋值,在nand flash驱动程序的probe函数里面利用了这里赋值的数据

platform_add_devices(smdk_devs,ARRAY_SIZE(smdk_devs)); //注册设备

s3c2410_pm_init();

}

二、注册驱动程序

1.为驱动程序编写platform_driver结构体

struct platform_driver {

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);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *); //设备恢复

struct device_driver driver;

};

2.注册platform_driver结构体

int platform_driver_register(struct platform_driver *);

platform设备注册例子:linux2.6.26.8内核中NAND FLASH设备驱动注册实例:

linux/drivers/mtd/nand/s3c2410.c

static struct platform_driver s3c2410_nand_driver = {

.probe = s3c2410_nand_probe,

.remove = s3c2410_nand_remove,

.suspend = s3c24xx_nand_suspend,

.resume = s3c24xx_nand_resume,

.driver = {

.name = "s3c2410-nand",

.owner = THIS_MODULE,

},

};

static int __init s3c2410_nand_init(void)

{

printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");

...

return platform_driver_register(&s3c2410_nand_driver); //注册平台设备

}

module_init(s3c2410_nand_init);

注:驱动程序绑定由内核自动执行,当内核发现一个驱动程序与一个设备匹配时,将调用驱动程序的probe函数,完成对设备的探测及初始化等工作。

详解Linux2.6内核中基于platform机制的驱动模型

http://blog.mcuol.com/User/jinxilee/Article/47466_1.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: