platform平台驱动模型简述(linux驱动开发篇)

此篇是驱动分离(总线、驱动和设备模型)的应用扩展,主要简述platform虚拟总线平台

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

  • 从Linux 2.6起引入了一套新的驱动管理和注册机制,platform_device和platform_driver,Linux中大部分的设备驱动都可以使用这套机制。

  • platform是一条虚拟的总线。设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性。

  • pltform机制本身使用并不复杂,由两部分组成:platform_device和platform_driver。不用设备树的时候通过platform机制开发底层驱动的大致流程为:定义platform_deive->注册platform_device->定义platform_driver->注册platform_driver。

  • 再使用设备树的时候,设备的描述被放到了设备树中,因此platform_device就不需要我去编写了,只需要实现platform_driver即可。

1. platform总线

总线(IIC、SPI、USB等),platform应该叫做虚拟总线

  • platfom总线的目的就是当我们将设备和驱动注册到虚拟总线上(内核)时,如果该设备是该驱动的设备,该驱动是该设备的驱动,在他们注册时,会互相寻找一次对方(只在注册的时候寻找一次,找完了就玩了)。
  • 注意:设备和驱动的关系是多对一的关系,即多个相同设备可使用一个driver,靠device(设备)中的id号来,好处是总线驱动是通用的
/******************************
***bus_type结构体表示总线*******
******************************/
 struct bus_type { 
	 const char      *name;                         /* 总线名字   */ 
     const char      *dev_name;                   
     struct device       *dev_root; 
     struct device_attribute *dev_attrs;  
     const struct attribute_group **bus_groups;    /* 总线属性 */ 
     const struct attribute_group **dev_groups;    /* 设备属性   */ 
     const struct attribute_group **drv_groups;    /* 驱动属性   */ 
    
     int (*match)(struct device *dev, struct device_driver *drv); //很重要*******
     int (*uevent)(struct device *dev, struct kobj_uevent_env *env); 
     int (*probe)(struct device *dev); 
     int (*remove)(struct device *dev); 
     void (*shutdown)(struct device *dev); 
   
     int (*online)(struct device *dev); 
     int (*offline)(struct device *dev); 

/*
@	platform总线是bus_type的一个具体实例
@	platform_bus_type就是platform平台总线
*/
struct bus_type platform_bus_type = { 
		.name         = "platform", 
     	.dev_groups   = platform_dev_groups, 
     	.match        = platform_match, 
     	.uevent       = platform_uevent, 
     	.pm           = &platform_dev_pm_ops, 
}

/*****************************************
@	匹配函数的四种方式
@	第一种:OF类型的匹配(设备树采用的匹配方式)
@	第二种:ACPI匹配方式
@	第三种:id_table匹配
@	第四种:比较驱动和设备的name字段,看看是不是相等(用的最多)
********************************************/

static int platform_match(struct device *dev, struct device_driver *drv) 
{ 
	struct platform_device *pdev = to_platform_device(dev); 
	struct platform_driver *pdrv = to_platform_driver(drv); 
   
	/*When driver_override is set,only bind to the matching driver*/ 
	if (pdev->driver_override) 	
	return !strcmp(pdev->driver_override, drv->name); 
   
    /* Attempt an OF style match first */ 
	if (of_driver_match_device(dev, drv)) 	
		return 1; 
      
    /* Then try ACPI style match */ 
	if (acpi_driver_match_device(dev, drv)) 
    	return 1; 
   
	/* Then try to match against the id table */ 
 	if (pdrv->id_table) 
 		return platform_match_id(pdrv->id_table, pdev) != NULL; 
  
    /* fall-back to driver name match */ 
    return (strcmp(pdev->name, drv->name) == 0); 
} 

2.platform_device

  • 如果内核支持设备树的话,我们就直接用设备树去描述,就可以不再使用platform_device来描述设备了
/*
@	platform_device 结构体
*/
struct platform_device { 
   const char  *name;   //保证device和driver的name is same
   int     id;              
   bool        id_auto; 
   struct device   dev; 
   u32     num_resources;   
   struct resource *resource; 
 
   const struct platform_device_id *id_entry; 
   char *driver_override; /* Driver name to force a match */ 
 
   /* MFD cell pointer */ 
   struct mfd_cell *mfd_cell; 
 
   /* arch specific additions */ 
   struct pdev_archdata    archdata; 
}; 


/******************
@	resource结构体
@	即是设备信息,比如外设寄存器等
@ 	start和end为资源的起始和终止信息,对于内存类资源就是起始和终止的地址
*******************/
    resource_size_t   start; 
    resource_size_t   end; 
    const char     *name; 
    unsigned long     flags; 
    struct resource   *parent, *sibling, *child; 
 }; 

/************
@	资源类型
@	上面的flag
***********/
 #define IORESOURCE_BITS         0x000000ff  /* Bus-specific bits */ 
  
 #define IORESOURCE_TYPE_BITS  0x00001f00  /* Resource type   */ 
 #define IORESOURCE_IO          0x00000100  /* PCI/ISA I/O ports */ 
 #define IORESOURCE_MEM         0x00000200 
 #define IORESOURCE_REG         0x00000300  /* Register offsets */ 
 #define IORESOURCE_IRQ         0x00000400 
 #define IORESOURCE_DMA         0x00000800 
 #define IORESOURCE_BUS         0x00001000 
... 
 /* PCI control bits.  Shares IORESOURCE_BITS with above PCI ROM.  */
 #define IORESOURCE_PCI_FIXED     (1<<4)  /* Do not move resource */ 
  • 下面是platform_device设备信息框架不支持设备树
  • 放在文件里描述了
/* 寄存器地址定义*/ 
#define PERIPH1_REGISTER_BASE    (0X20000000) /* 外设 1 寄存器首地址 */ 
#define PERIPH2_REGISTER_BASE    (0X020E0068) /* 外设 2 寄存器首地址 */ 
#define REGISTER_LENGTH           4 
 
/* 资源 */ 
static struct resource xxx_resources[] = { 
   [0] = { 
         .start  = PERIPH1_REGISTER_BASE, 
       .end    = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1), 
       .flags  = IORESOURCE_MEM, 
   },   
   [1] = { 
          .start  = PERIPH2_REGISTER_BASE, 
       .end    = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1), 
       .flags  = IORESOURCE_MEM, 
   }, 
}; 
 
/* platform 设备结构体 */ 
static struct platform_device xxxdevice = { 
   .name = "xxx-gpio", 
   .id = -1, 
   .num_resources = ARRAY_SIZE(xxx_resources), 
   .resource = xxx_resources, 
}; 
      
/* 设备模块加载 */ 
static int __init xxxdevice_init(void) 
{ 
   return platform_device_register(&xxxdevice); 
} 
 
/* 设备模块注销 */ 
static void __exit xxx_resourcesdevice_exit(void) 
{ 
   platform_device_unregister(&xxxdevice); 
} 
 
module_init(xxxdevice_init); 
module_exit(xxxdevice_exit); 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("zuozhongkai"); 

3. platform_driver

  • 总的来说,platform驱动还是传统的字符设备、块设备驱动或者网络设备驱动。
  • 只是套上了一张“platform的皮”
  • 目的是为了是使用总线、驱动和设备这个驱动模型来实现驱动的分离和分层。
/**************************************
@	platform_driver结构体表示platform驱动
***************************************/
 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 (*resume)(struct platform_device *); 
     struct device_driver driver; //是一个类
     const struct platform_device_id *id_table; //第三种匹配方式
     bool prevent_deferred_probe; 
 };

/**************************
@	platform_device_id结构体
***************************/
 struct platform_device_id { 
     char name[PLATFORM_NAME_SIZE]; 
     kernel_ulong_t driver_data; 
 }; 
 
/*********************
@	device_driver结构体
**********************/
  struct device_driver { 
  const char          *name; 
  struct bus_type       *bus; 
   
  struct module         *owner; 
  const char          *mod_name;  /* used for built-in modules */ 
   
  bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */ 
   
  const struct of_device_id       *of_match_table; //用设备树的时候驱动使用的匹配表,同样是数组,
  const struct acpi_device_id     *acpi_match_table; 
  
  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); 
  const struct attribute_group **groups; 
  
  const struct dev_pm_ops *pm; 
  
  struct driver_private *p; 
 }; 
 
/**********************
@	of_device_id 结构体 
**********************/
 struct of_device_id { 
   char        name[32]; 
   char        type[32]; 
   char        compatible[128]; //非常重要,设备树相关
   const void   *data; 
 }; 

下面是platform驱动框架

/* 设备结构体 */ 
  struct xxx_dev{ 
    struct cdev cdev; 
    /* 设备结构体其他具体内容 */ 
  }; 
  
  struct xxx_dev xxxdev;   /* 定义个设备结构体变量 */ 
  
  static int xxx_open(struct inode *inode, struct file *filp) 
  {     
    /* 函数具体内容 */ 
    return 0; 
   } 
  
 static ssize_t xxx_write(struct file *filp, const char __user *buf, 
size_t cnt, loff_t *offt) 
   { 
    /* 函数具体内容 */ 
    return 0; 
   } 
  
 /* 
  * 字符设备驱动操作集 
 */ 
  static struct file_operations xxx_fops = { 
   .owner = THIS_MODULE, 
   .open = xxx_open, 
   .write = xxx_write, 
  }; 
 
/* 
 * platform 驱动的 probe 函数 
 * 驱动与设备匹配成功以后此函数就会执行 
 */ 
  static int xxx_probe(struct platform_device *dev) 
  {     
   ...... 
   cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */ 
   /* 函数具体内容 */ 
   return 0; 
  } 
 
  static int xxx_remove(struct platform_device *dev) 
  { 
   ...... 
   cdev_del(&xxxdev.cdev);/*  删除 cdev */ 
   /* 函数具体内容 */ 
   return 0; 
  } 
 
  static int xxx_remove(struct platform_device *dev) 
  { 
   ...... 
   cdev_del(&xxxdev.cdev);/*  删除 cdev */ 
   /* 函数具体内容 */ 
   return 0; 
  } 
  
/* 匹配列表 */ 
static const struct of_device_id xxx_of_match[] = { 
   { .compatible = "xxx-gpio" }, 
     { /* Sentinel */ } 
}; 
 /*  
 * platform 平台驱动结构体 
 */ 
  static struct platform_driver xxx_driver = { 
   .driver = { 
       .name       = "xxx", 
       .of_match_table = xxx_of_match, 
   }, 
   .probe      = xxx_probe, 	
   .remove     = xxx_remove, 
  }; 
   
  /* 驱动模块加载 */ 
  static int __init xxxdriver_init(void) 
  { 
   return platform_driver_register(&xxx_driver); 
  } 
 
  /* 驱动模块卸载 */ 
  static void __exit xxxdriver_exit(void) 
  {  
       platform_driver_unregister(&xxx_driver); 
  } 
 
  module_init(xxxdriver_init); 
  module_exit(xxxdriver_exit); 
  MODULE_LICENSE("GPL"); 
  MODULE_AUTHOR("zuozhongkai"); 
  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值