设备注册与驱动注册

1.platform虚拟总线

总线设备驱动模型主要包含总线、设备、驱动三个部分,总线可以是一条真实存在的总线,例如USB、I2C等典型的设备。但是对于一些设备(内部的设备)可能没有现成的总线。Linux 2.6内核中引入了总线设备驱动模型。总线设备驱动模型与之前的三类驱动(字符、块设备、网络设备)没有必然的联系。设备只是搭载到了总线中。在linux内核中假设存在一条虚拟总线,称之为platform总线。总线设备驱动模型主要包含总线、设备、驱动三个部分,总线可以是一条真实存在的总线,例如USB、I2C等典型的设备。但是对于一些设备(内部的设备)可能没有现成的总线。Linux 2.6内核中引入了总线设备驱动模型。总线设备驱动模型与之前的三类驱动(字符、块设备、网络设备)没有必然的联系。设备只是搭载到了总线中。在linux内核中假设存在一条虚拟总线,称之为platform总线。

platform总线相比与常规的总线模型其优势主要是platform总线是由内核实现的,而不用自己定义总线类型,总线设备来加载总线。platform总线是内核已经实现好的。只需要添加相应的platform device和platform driver。
这里写图片描述

查看linux系统的总线ls /sys/bus
查看linux系统的设备cat /proc/devices
这里写图片描述
这里写图片描述

2.Linux驱动和设备的注册过程

Linux 内核会要求每出现一个设备就要向总线汇报,或者说注册,出现一个驱动,也要向总线汇报,或者叫注册。
在系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立一个struct_device 的变量,然后将设备的变量插入到devices 链表系统初始化任意一个驱动程序的时候,就要准备一个struct device_driver 结构的变量,然后将驱动的变量插入到drivers 链表
Linux 总线是为了将设备和驱动绑定,方便管理。在系统每注册一个设备的时候,会寻找与之匹配的驱动(后面的热拔插设备会用到这个注册流程);相反,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
这里写图片描述

在注册驱动的时候,系统会通过platform_match 函数匹配设备和驱动。注册设备的结构体为platform_device,注册驱动的结构体为platform_driver。设备和和驱动结构体的成员name 字段,相同则匹配如果匹配了则会调用platform_driver 中的probe 函数,注册驱动。

3.设备节点

上层应用使用设备节点访问对应的设备
ls /dev
上层应用有一套标准的接口文件和函数用来和底层通信。Linux 将所有对设备的操作全部都抽象为对文件的操作,常用的文件操作函数有open、close、write、read、write 等。

小结

注册设备,注册驱动和节点。

4.注册流程

4.1.设备注册(步骤一)

platform结构体

内核文件include/linux/platform_device.h文件中查看platform_device.

struct platform_device{
    const char *name,//设备名称,在sys/devices会显示
    int id;//设备id,用于插入总线并具有相同的name的设备编号,只有一个则是-1;
    struct device dev;//结构体内嵌device结构体
    u32 num_resources;//设备使用的资源数量
    struct resource *resource;//设备组使用的资源数组
    const struct platform_device_id *id_entry;

    struct mfd_cell *mfd_cell;//维护功能配置单元的指针

    struct pdev_archdata archdata;//具体添加

};

添加设备到平台总线

(1)注册设备添加结构体

在内核源码中arch/arm/mach-exynos/mach-itop4412.c注册平台设备结构体“platform_device”中,只调用了两个参数*name”和“id”。仿照着已有的leds这段代码在它前面添加一个设备“hello_ctl”,

#ifdef CONFIG_LEDS_CTL
struct platform_device s3c_device_leds_ctl = {
    .name   = "leds",
    .id             = -1,
};

#endif
#ifdef CONFIG_HELLO_CTL
struct platform_device s3c_device_hello_ctl = {
.name   = "hello_ctl",
.id             =-1,
    };
#endif

(2)在Kconfig中添加宏定义

在drivers/char/Kconfig中添加

   config HELLO_CTL
    tristate "Enable HELLO config"
    default y
    help
        Enable HELLO config

tristate代表三态,y,n,m。

(3)enable设备

在内核源码中使用命令“mak menuconfig”,进入“Device Drivers—>”→“Character devices —>”→“Enable HELLO config”

(4)添加编译条件命令

进入arch/arm/mach-exynos/mach-itop4412.c中,添加设备初始化代码
仿照LED添加

    #ifdef CONFIG_LEDS_CTL
        &s3c_device_leds_ctl,
    #endif
    #ifdef CONFIG_HELLO_CTL
        &s3c_device_hello_ctl,
    #endif

编译内核烧写即可开发板启动之后,使用命令“ls /sys/devices/platform/”可以查看到新注册的hello 设备。

4.2.驱动注册(步骤二)

注册驱动的时候需要和设备匹配,简单介绍了将驱动注册到平台设备的结构体“platform_driver_register”在内核源码中include/linux/device.h文件中

extern int platform_driver_register(struct platform_driver *);
    extern void platform_driver_unregister(struct platform_driver *);
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 (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};

该结构中包含了一组操作函数和一个struct device_driver 的对像。在驱动中首先要做的就是定义platform_driver 中的函数,并创建这个结构的一个对象实例, 然后在init()函数中调用platform_driver_register()向系统注册我们的驱动。
函数int (probe)(struct platform_device );
主要是进行设备的探测和初始化。例如我们想调用一个GPIO,那么首先需要探测这个GPIO 是否被占用了,如果被占用了那么初始化失败,驱动注册也就失败了;如果没有被占用,那么我们就申明要占用它。函数中一般还会添加生成设备节点的函数,如果初始化成功,那么就会需要添加设备节点。
函数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;
主要包含两个参数,一个是name参数,驱动名称(需要和设备驱动结构体中的name 参数一样),
一个是owner,一般是THIS_MODULE。

1.添加“#include <linux/platform_device.h>”,
然后定义一个宏变量DRIVER_NAME,定义为“hello_ctl”,需要和前面注册的hello 设备的名称相同。
2.模块入口和出口调用函数platform_driver_register 和
platform_driver_unregister,如下图所示,先将参数名定义为“&hello_driver”。,定义结构体“hello_driver”。
struct platform_driver hello_driver = {
    .probe = hello_probe,
    .remove = hello_remove,
    .shutdown = hello_shutdown,
    .suspend = hello_suspend,
    .resume = hello_resume,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
    }
};
3.然后定义函数hello_probe、hello_remove、hello_shutdown、hello_suspend、hello_resume。**(需要在结构体前定义声明)**

示例代码

#include <linux/init.h>
#include <linux/module.h>

/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>

#define DRIVER_NAME "hello_ctl"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");

static int hello_probe(struct platform_device *pdv){

    printk(KERN_EMERG "\tinitialized\n");

    return 0;
}

static int hello_remove(struct platform_device *pdv){

    return 0;
}

static void hello_shutdown(struct platform_device *pdv){

    ;
}

static int hello_suspend(struct platform_device *pdv){

    return 0;
}

static int hello_resume(struct platform_device *pdv){

    return 0;
}

struct platform_driver hello_driver = {
    .probe = hello_probe,
    .remove = hello_remove,
    .shutdown = hello_shutdown,
    .suspend = hello_suspend,
    .resume = hello_resume,
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
    }
};


static int hello_init(void)
{
    int DriverState;

    printk(KERN_EMERG "HELLO WORLD enter!\n");
    DriverState = platform_driver_register(&hello_driver);

    printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
    return 0;
}

static void hello_exit(void)
{
    printk(KERN_EMERG "HELLO WORLD exit!\n");

    platform_driver_unregister(&hello_driver);  
}

module_init(hello_init);
module_exit(hello_exit);
  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值