Linux设备驱动

1.平台总线模型

Linux设备驱动模型有三大实体
(1)设备:管理设备自身的属性信息(寄存地址或者led个数),对应的数据结构随类型而变,但有共同的属性成员 struct device。
(2)驱动:实现设备的具体硬件操作(驱动方法),对应的数据结构随类型而变,但有共同的属性成员 struct device_driver;
(3)总线:绑定了符合该总线要求(匹配词)的设备端和驱动端,将设备和驱动匹配起来,对应的数据结构是struct bus_type。
 补充:多个类似的设备可以匹配同一个驱动。也有些设备与驱动时一对一匹配的。

  • Linux内核维护着一个全局的设备链表--加载的设备均会挂载到这个链表上,挂载了所有的已安装的设备;
  • Linux内核维护着一个全局的驱动链表--指的就是对应的驱动程序,挂载了所有的已安装的驱动程序。
  • 每当安装一个驱动时,相应的总线把该驱动和设备链表上的每个设备的匹配词进行比较,如果匹配则绑定该驱动,并把设备的信息(要传递的参数)传递给驱动。
  • 每当安装一个设备时,相应的总线把该设备和驱动链表上的每个驱动的匹配词进行比较,如果匹配则绑定该设备,并把设备的信息(要传递参数)传递给驱动。

        常见的专用总线:I2C、SPI、USB、PCI、CAN.....


2. platform_device

2.1 相关结构体

#include <linux/platform_device.h>

struct platform_device{
    const char *name;    //自定义的设备名,用于和驱动匹配的匹配词(关键词)。设备端的匹配词
    int  id;            //当设备和驱动一对一匹配时,设为-1
                        //注册成功后内核会自动创建目录 /sys/bus/platform/devices/name/
    struct device dev;    //所有设备的公共抽象模型(类似基类),很重要
    struct resource *resources;  //设备所用资源数组的首地址
    u32 num_resources;    //设备所用资源的个数,即资源数组的元素个数
    struct platform_device_id *id_entry;  //设备id表入口,由内核实现
    struct pdev_archdata  archdata;
};

struct device{
    //平台数据指针,指向描述设备本身的属性信息(platform_device一级成员无法表示的情形),                        
    platform_driver模块中可以获取该数据
    void  *plarform_data;
    //指向描述驱动的数据,通常在platform_device模块中设置该数据,用于告知platform_driver要服务的设备类型,在platform_driver模块中获取该数据,提供相应对应的驱动服务
    void  *driver_data;
    .....
    void  (*release)(struct device *dev);  //指向释放device对象的函数,必须实现(哪怕是空操作也行)
                                     //否则卸载模块时候报错
                                     //当device对象应用计数为0时会自动调用该函数
}
 
struct resource{
    resource_size_t start;   //所用资源的起始物理地址或编号 例如:GPIO0--0xFF72 0000
    resource_size_t end;    //所用资源的结束物理地址或编号 66536
    const char *name;     //自定义的资源名称
    unsigned long flags;   //资源类型
    struct resource *parent,*sibling,*child;  //父节点、兄节点、子节点,内核实现
};
 
资源类型:
#define IORESOURCE_IO  0x00000100  //X86为所有的I/O外设设置独立的内存空间隔离
#define IORESOURCE_MEM  0x00000200  //内存空间
#define IORESOURCE_IRQ    0x00000400 //中断编号
#define IORESOURCE_DMA   0x00000800 //DMA通道编号

2.2 相关函数或者宏

1.platform_device_register()
头文件:#include <linux/platform_device.h>
原型:int  platform_device_register(struct platform_device *pdev);  //用在函数的入口
功能:注册指定的platform_device
参数:pdev:指定的platform_device对象的地址
返回值:成功:0    
        失败:负的错误码

2.platform_device_unregister()
头文件:#include <linux/platform_device.h>
原型:void  platform_device_unregister(struct platform_device *pdev);  //用在函数的出口
功能:注销指定的platform_device
参数:Pdev:指定的platform_device对象的地址
返回值:无

2.3 编程

平台设备驱动代码的思路:

  1. 如果用到资源,则定义并初始化一个struct resource类型的数组。否则忽略该步骤。
  2. 定义并初始化一个struct platform_device类型的对象,填充其中的name、id、resource(不用则填NULL)、num_resources(不用填0)成员 struct device-->release成员  dev->platform_data根据实际情况进行实现。
  3. 通常在模块的加载函数中调用platform_device_register()注册指定的platform_device对象。
  4. 通常在模块的卸载函数中调用platform_device_unregister()注销指定的platform_device对象。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
 
 
#define rGPIO1        0xFF730000    //GPIO1的物理地址 GPIO0的物理地址0xFF720000
#define rGPIO1SIZE    0xFFFF        //GPIO1占用的大小
 
 
//释放led的资源 
static void led_release(struct device *dev)
{
	printk(KERN_EMERG "%s is call\r\n", __FUNCTION__);
}
 
//定义设备资源的结构体的实现
static struct resource led_resource[] = {
    [0] = {
        .start = rGPIO1,
        .end   = rGPIO1 + rGPIO1SIZE - 1,
        .flags = IORESOURCE_MEM,
    },
};
 
//设备端的核心结构体的实现--配置的具体实现
static struct platform_device led_dev = {
    .name = "rk3399",     //匹配词
    .id   = -1,
    .num_resources = ARRAY_SIZE(led_resource),
    .resource = led_resource,
    .dev = {
        .release = led_release,
    },
};
 
 
//模块的入口
static int __init xxx_init(void)
{   
	printk(KERN_EMERG "%s is call\n",__FUNCTION__);
	//第一注册设备端代码实现
    platform_device_register(&led_dev);
    return 0;
}
 
//模块的出口
static void __exit xxx_exit(void)
{
	printk(KERN_EMERG "%s is call\n",__FUNCTION__);
	//注销指定的设备
	platform_device_unregister(&led_dev);
}
 
 
//注册一下
module_init(xxx_init);
module_exit(xxx_exit);
 
//开源协议
MODULE_LICENSE("GPL");

3. platform_driver

3.1相关结构体

#include <linux/platform_device.h>
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;      //所有驱动的公共抽象模型(类似基类),很重要
    struct platform_device_id *id_table;  //匹配设备的id数组地址,该数组最末元素必须为{};
                                          //一个驱动可以匹配一个或多个设备,描述可以匹配哪些设备
};
 
struct device_driver{
    const char *name;   //自定义的驱动名,用于和设备一对一匹配的匹配词--module模块。
                        //注册成功后内核会自动创建目录/sys/bus/platform/drivers/name
    const struct of_device_id  *of_match_table;  //用于设备侧以设备树方式实现一对多匹配
                                              //匹配设备的数组首地址,该数组最末元素必须为{};
                                        //一个驱动可以匹配一个或多个设备,描述可以匹配哪些设备
}
 
struct of_device_id{
    char name[32];    //用于以设备树方式实现设备进行一对一或一对多匹配的关键词
                      // 前提条件:platform_device是以设备树的方式实现
    char type[32];
    char compatible[128];
    const void *data;   
};
 
struct platform_device_id{
   char name[PLATFORM_NAME_SIZE];   //用于和设备进行一对一或一对多匹配的匹配词
                                          //前提:platform_device是以module的方式实现
   kernel_ulong_t  driver_data;
};

3.2 相关函数或者宏

1.platform_driver_register()
头文件:#include <linux/platform_device.h>
原型:int platform_driver_register(struct platform_driver *drv);
功能:注册指定的platform_driver--核心结构体对象的名字
参数:Drv:指定的platform_driver对象的地址
返回值:成功:0      
        失败:负的错误码

内核源码的基本形式
#define platform_driver_register(drv) \
    __platform_driver_register(drv, THIS_MODULE)


2.platform_driver_unregister()
头文件:#include <linux/platform_device.h>
原型:void platform_driver_unregister(struct platform_driver *drv);
功能:注销指定的platform_driver
参数:drv:指定的platform_driver对象的地址
返回值:无

内核源码的基本形式:
/**
 * platform_driver_unregister - unregister a driver for platform-level devices
 * @drv: platform driver structure
 */
void platform_driver_unregister(struct platform_driver *drv)
{
    driver_unregister(&drv->driver);
}

3.platform_get_resource()
头文件:#include <linux/platform_device.h>
原型:struct resource* platform_get_resource(struct platform_device *dev,unsigned int type,unsigned int num);
功能:查看指定的platform_device所使用的资源(能查到什么资源--(一般情况下传递是寄存器的地址))
参数:Dev:指定的platform_device对象的地址
      type:查看资源的类型
      num:查看的资源在所用的同类型资源中的编号(排号),从0开始编号
返回值:成功:查到的资源对象的首地址   
        失败:NULL

内核源码的基本形式:
/**
 * platform_get_resource - get a resource for a device
 * @dev: platform device
 * @type: resource type
 * @num: resource index
 */
struct resource *platform_get_resource(struct platform_device *dev,
                       unsigned int type, unsigned int num)
{
    int i;
 
    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];
 
        if (type == resource_type(r) && num-- == 0)
            return r;
    }
    return NULL;
}

4.platform_get_irq()
头文件:#include <linux/platform_device.h>
原型:Int platform_get_irq(struct platform_device *dev,unsigned int num);
功能:查看指定的platform_device所使用的IRQ号资源
参数:dev:指定的platform_device对象的地址  
      num:查看的资源在所用的IRQ号资源的编号(排号),从0开始编号
返回值:成功 :>=0  查到的IRQ号  
        失败:负的错误码

5.platform_get_resource_byname()
头文件:#include <linux/platform_device.h>
原型:stuct resource *platform_get_resource_byname(struct platform_device *dev,unsigned int type,const char *name);
功能:根据资源名称查看指定platform_device所使用的资源。
参数:dev:指定的platform_device对象的地址
      type:查看资源的类型
      name:查看资源的名称
返回值:成功:查到的资源对象的地址  
        失败:NULL

内核源码的基本形式:
/**
 * platform_get_resource_byname - get a resource for a device by name
 * @dev: platform device
 * @type: resource type
 * @name: resource name
 */
struct resource *platform_get_resource_byname(struct platform_device *dev,
                          unsigned int type,
                          const char *name)
{
    int i;
 
    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];
 
        if (unlikely(!r->name))
            continue;
 
        if (type == resource_type(r) && !strcmp(r->name, name))
            return r;
    }
    return NULL;
}

6.platform_get_irq_byname()
头文件:#include <linux/platform_device.h>
原型:int platform_get_irq_byname(struct platform_device *dev,const char *name);
功能:根据资源名称查看指定的platform_device所使用的IRQ号的资源
参数:dev:指定的platform_device对象的地址  
      name:查看的IRQ号资源的名称
返回值:成功 :>=0  查看到的IRQ号    
        失败:负的错误码

7.devm_request_mem_region()
头文件:#include <linux/ioport.h>
宏原型:devm_request_mem_region(dev,start,n,name);
功能:向内核申请连续的内存资源,通告其他驱动,这段内存已经被使用。
参数:dev:当前申请资源的设备的struct device成员的地址
      start:申请内存资源的起始物理地址,类型是resource_size_t
      n:申请内存大小(字节数)
      name:自定义的名称,不重要  
返回值:成功:struct resource *类型的有效地址
        失败:NULL
注意:
        /proc/iomem文件中记录了被注册的外设和物理地址

8.devm_release_mem_region()
头文件:#include <linux/ioport.h>
宏原型:devm_release_mem_region(dev,start,n);
功能:在内核释放之前申请内存资源
参数:dev:当前申请资源的设备的struct device成员的地址
      start:申请内存资源的起始物理地址,类型是resource_size_t
      n:申请内存大小(字节数)
返回值:无

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值