在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