Linux Platform驱动程序框架解析

来源:互联网 发布:c 定义常量数组 编辑:程序博客网 时间:2024/05/24 00:13

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

原创粉丝点击