驱动开发框架学习2

1.总线设备驱动的概念以及总线上注册设备和驱动(非设备树)

Linux kernel 字符驱动框架

总线、设备、驱动  => 增加驱动的可扩展性和可移植性

思想:
将设备的信息从驱动中分离出来,我们需要在操作系统中,添加设备和驱动两部分。
设备中包含是设备的信息(资源),驱动中包含的是操作设备函数接口。
为了能让驱动最终能操作我们的硬件设备,我们在驱动中必须获取设备的信息(资源)。

问题:设备和驱动是分离的,那么驱动是如何获取具体设备信息的呢?
回答:
	设备和驱动都会注册到总线上,当注册设备的时候,会去寻找同名的驱动
	当注册驱动的时候,也会去找同名的设备。相互查找。一旦匹配成功,操作
	系统就会自动调用驱动提供的probe函数。我们只需要在probe函数中,使用
	操作系统提供的通用API获取硬件的信息即可。
	

问题:Linux 系统中"总线"如何理解?
回答:
	在实际的硬件上,总线种类可以分成两大类 
	1.平台总线 (CPU核与硬件控制器之间的通信),挂载都是控制器设备
		 platform bus 
		 	
    2.边缘设备之间通信的总线,挂载是符合总线时序的外围设备
      i2c , spi ,usb , uart ....
      不同的边缘设备之间通信的总线,总线时序是不一样的,
      对于这些总线,Linux 内核是单独实现的 
     	
      总线在操作系统中本质就是两个链表:挂载设备的链表和挂载驱动的链表
     	
	
问题:在Linux 2.6内核之后,应该如何写驱动?
回答:
	根据驱动的设备,给予总线来写驱动	
	
		
问题:如何给予总线来写驱动?
回答:
	  (1)根据自己的设备,来确定总线的类型
	     platform bus / i2 bus /usb bus/....
	  (2)根据总线的类型, 确定设备在总线上如何描述(结构体) 
	     struct platform_device{
	     	设备名;
	        设备的资源
	        通用的设备描述(struct device : platform_data->记录设备私有的信息)
	        id_entry:当设备和驱动的id_table中某一个成员匹配上的时候,这个成员就会记录id_table中匹配上的成员地址
	     };
	     
	     struct resource{
	        资源的开始
	        资源的结束
	        资源的类型:IO资源(寄存器地址)  中断资源(中断号)   DMA资源(通道)
	     };
	     
	  (3)根据总线的类型, 确定驱动在总线上如何描述 
	    struct platform_driver{
	        probe函数 :设备和驱动匹配的时候,操作系统自动调用
	        remove函数:设备和驱动分离的时候,操作系统自动调用
	        
	        通用的驱动描述(struct device_driver : 这里面可以记录驱动的名字)
	        
	        id_table : 当前驱动支持平台设备(记录支持的平台设备名字)
	    };
	      
	  (4)根据总线的类型,确定在总线上如何注册设备  i2c控制器驱动
	     int platform_device_register(struct platform_device *pdev);
	     
	  (5)根据总线的类型,确定在总线上如何注册驱动  i2c_add_driver
	     int platform_driver_register(struct platform_driver *pdriver);
	     
	  (6)根据总线的类型, 确定设备和驱动匹配原则
	     如果驱动提供了id_table,那就拿设备的名字和id_table中记录的名字匹配
	     如果驱动没有提供id_table,那就拿设备的名字和驱动的名字进行匹配
	    
	  (7)一旦设备和驱动匹配后,操作系统就会调用驱动提供的probe函数。
	  	 在这个函数中,一般需要做两件事情:
	  	 <1>获取匹配的硬件资源 
	  	 <2>注册字符设备(可选)


问:驱动和设备匹配上之后,驱动如何从能获取设备的信息?
答:驱动和设备匹配上之后,操作系统会自动调用驱动提供的probe函数,并且会把设备的结构体传递过来
   ,这个结构体中记录设备的所有信息

--------------------------------------------------------------------------------------
1.fs4412 上的LED设备对应的总线类型是什么?
	platform bus 
	
2.在这个总线上,设备用什么结构体描述?
	struct platform_device{
			char *name;
			int id;
			struct device dev;//platform_data 这个指针设备的私有资源信息
			int number_resources;
			struct resource *resource;
			....
	};
	
	struct resource{
			int start;
			int end;
			int flags;
			...
	};
	
	例如:fs4412 LED资源描述
	struct resource fs4412_led_resource[] = {
		 [0] = {
		 		.start =  0x11000C40,
		 		.end   =  0x11000C40 + 8 - 1,
		 		.flags =  IORESOURCE_MEM,
		 },
	
	};
	
	struct platform_device fs4412_device_led = {
			.name = "fs4412-led",
			.id   = -1,
			.num_resources = ARRAY_SIZE(fs4412_led_resource),
			.resource = fs4412_led_resource,
	};
	

3.在这个总线上,驱动用什么结构体描述?
	struct platform_driver{
			.probe  = xxx_probe,
			.remove = xxx_remove,
			.id_table = xxx_table,
			.driver  = {
				.name = "fs4412-led",
				.owner = THIS_MODULE,
			},
	};
	
	struct platform_device_id  xxx_driver_ids[] = {
		 {
		 		.name = "xxx",
		 		.driver_data = 0;
		 },
		 ....
	
		{},
	};
	

4.在这个总线上,如何注册设备?
  int platform_device_register(struct platform_device *dev);

5.在这个总线上,如何注册驱动?
	int platform_driver_register(struct platform_driver *drv);

6.在这个总线上,驱动和设备的匹配规则是怎样的?
	如果驱动有id_table,则用设备的名字和id_table记录的名字比较
	如果驱动没有id_table,则用设备的名字和驱动的名字进行比较
	
	问:id_table是什么鬼?
	答:驱动可以支持设备名字列表,实际上一个数组,记录是一些字符串
		 
	问:如果设备和id_table的成员匹配上,这个时候做一些以什么?
	答:platform_device结构体中的id_entry记录id_table成员的地址。
		 	 
7.在这个总线上,驱动和设备匹配后,调用的probe函数中,如何获取硬件资源?	 
	struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,int num);
	
	@dev 平台设备的结构体 
	@type 资源的类型
	@num  同类型资源的编号
		

练习:基于platform bus编写LED驱动	
	
(1)注册设备
	 1.定义设备结构体
	 	 struct platform_device  fs4412_led_device
	 2.填充设备结构体
	 	 <1>设备的名字
	 	 <2>设备的资源
	 	 
	 3.注册	
	 	  platform_device_register
	 	  

(2)注册驱动
	1.定义驱动结构体
		struct platform_driver led_driver
		
  2.填充驱动结构体 
  	<1>驱动名字
  	<2>probe函数
  		 [1]获取设备的资源
  		 [2]注册字符设备
  	<3>remove函数
  	<4>id_table (可选)			 	  

 	3.注册
 		platform_driver_register
 		
-----------------------------------------------------------------------------------------------------
给予总线写驱动思路

1.模块化编程
  <1>头文件
  <2>许可权限
  <3>模块的入口和出口
  

2.在总线上注册驱动

<1>填充结构体
struct platform_driver  xxx_driver = {
	.probe    = xxx_probe,
	.remove   = xxx_remove,
	.id_table = xxx_table,
	.driver   = {
			.owner = THIS_MODULE,
			.name  = "xxxx",
	},

};
  
<2>注册结构体
   在模块的入口函数注册结构体
   platform_driver_register
   
   在模块的出口函数注销结构体
   platform_driver_unregister
   
   
3.总线上的设备和驱动相互匹配后,操作系统自动调用驱动提供的probe函数
  实现probe函数
  
  int xxx_probe(struct platform_device *pdev)
  {
  		1.获取资源
  			platform_get_resource
  			
  		2.注册字符设备
  			[1]初始化cdev结构体 (让cdev结构体记录设备的操作函数接口)
  				 cdev_init
  				 
  		  [2]申请设备号
  		  	 第一种方式 (静态):
  		  	 		register_chrdev_region
  		  	 		
  		  	 第二种方式 (动态):
  		  	 		alloc_chrdev_region
  		  	 		
  		  [3]添加字符设备
  		  	 cdev_add	 				
  		  
  		  ----------------自动创建设备节点------------------------------------------
  		  [4]创建类
  		  	 class_create
  		  	
  		  [5]创建设备
  		  	 device_create	 	 		 
  }
   
   
4.总线上的设备和驱动分离后,操作系统自动调用驱动提供的remove函数
  实现remove函数
  int xxx_remove(struct platform_device *pdev)  
  {
  		核心思想:将probe函数申请的资源,释放掉
  
  	  <1>删除设备
  	  	 device_destory
  	  	 
  	  <2>删除类
  	  	 class_destory
  	  	 
  	  <3>删除字符设备
  	  	 cdev_del
  	  	 
  	   <4>释放设备号
  	   	  unregister_chrdev_region	

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值