博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux Platform驱动程序框架解析
阅读量:2456 次
发布时间:2019-05-10

本文共 4307 字,大约阅读时间需要 14 分钟。

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://thnhb.baihongyu.com/

你可能感兴趣的文章
kotlin键值对数组_Kotlin程序以升序对数组进行排序
查看>>
Java FileDescriptor sync()方法与示例
查看>>
Java PriorityQueue clear()方法与示例
查看>>
system getenv_Java System类getenv()方法及示例
查看>>
python 示例_带有示例的Python字典update()方法
查看>>
java 方法 示例_Java ArrayDeque offerFirst()方法与示例
查看>>
stringwriter_Java StringWriter toString()方法与示例
查看>>
JavaScript中的位置协议属性
查看>>
一个使用numpy.ones()的矩阵| 使用Python的线性代数
查看>>
Java IdentityHashMap keySet()方法及示例
查看>>
Java RandomAccessFile close()方法与示例
查看>>
Java StringBuilder getChars()方法与示例
查看>>
Java Duration类| isNegative()方法与示例
查看>>
Java LocalDate类| ofYearDay()方法与示例
查看>>
java 方法 示例_Java集合checkedList()方法与示例
查看>>
Java IdentityHashMap isEmpty()方法与示例
查看>>
long类型20位示例_Java Long类lowerOneBit()方法与示例
查看>>
kotlin 判断数字_Kotlin程序检查数字是偶数还是奇数
查看>>
doublevalue_Java Double类doubleValue()方法与示例
查看>>
Java Double类parseDouble()方法的示例
查看>>