Linux设备驱动开发

 内核模块:

 

内核模块功能介绍

    Linux 提供了这样的一种机制:编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码可被动态地加载到内核中;这种机制被称为模块(Module)。模块具有以下特点。

1、模块本身不被编译入内核映像,从而控制了内核的大小。

2、模块一旦被加载,它就和内核中的其他部分完全一样.

常用命令

    1、insmod:加载内核模块,譬如insmod ./hello.ko

    2、modprobe:modprobe命令比insmod命令要强大,

    它在加载某模块时会同时加载该模块所依赖的其他模块。

    3、lsmod:获得系统中加载了的所有模块以模块间的依赖关系,该命令通过读取/proc/modules文件获取信息。

    4、modinfo:使用 modinfo <模块名>命令可以获得模块的信息,包括模块的作者、模块的说明、模块所支持的参数以及vermagic。

 

内核模块程序结构

一个Linux 内核模块主要由以下几个部分组成。

1、模块加载函数(init必须)。

当通过 insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相

关初始化工作。

2、模块卸载函数(exit必须)。

当通过 rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块加载函数相反的功能。

3、模块许可证声明(必须)。

模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染

(kernel tainted)的警告。在 Linux 2.6 内核中,可接受的LICENSE 包括“GPL”、“Dual BSD/GPL”等。大多

数情况下,内核模块应遵循GPL 兼容许可权。

注:不声明模块许可,会导致无法会使用内核提供的众多API.

4 模块参数(可选)。

模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。

5 模块导出符号(可选)。

内核模块可以导出符号(symbol,对应于函数或变量),这样其他模块可以使用本

模块中的变量或函数。

6、模块作者等信息声明(可选)。

 

Linux设备总体介绍

n  字符设备

    一个字符( char ) 设备是一种可以当作一个字节流来存取的设备( 如同一个文件 ); 一个字符驱动负责实现这种行为. 这样的驱动常常至少实现 open, close, read, 和 write 系统调用.

n  块设备

      块设备通过位于 /dev 目录的文件系统结点来存取. 一个块设备(例如一个磁盘)应该是可以驻有一个文件系统的。块设备只能处理这样的 I/O 操作: 传送一个或多个长度经常是 512 字节( 或一个更大的 2 的幂的数 )的整块。

n  网络设备

     任何网络事务都通过一个接口来进行;网络接口负责发送和接收数据报文, 在内核网络子系统的驱动下, 不必知道单个事务是如何映射到实际的被发送的报文上的. 很多网络连接( 特别那些使用 TCP 的)是面向流的, 但是网络设备却常常设计成处理报文的发送和接收. 一个网络驱动对单个连接一无所知; 它只处理报文.

 

 http://image.huawei.com/tiny-lts/v1/images/286c62629eeca134ebe1_376x547.png@900-0-90-f.png

字符设备驱动组成

 http://image.huawei.com/tiny-lts/v1/images/2ccef2629eede5ec7e51_872x184.png@900-0-90-f.png

块设备相关组件框架

 

http://image.huawei.com/tiny-lts/v1/images/695db2629ef3d6461042_336x552.png@900-0-90-f.png
 

 

Linux网络设备体系结构

 

http://image.huawei.com/tiny-lts/v1/images/9746c2629ef070ce0eb4_416x576.png@900-0-90-f.png
 

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

基于Platform总线的驱动开发流程如下

a -- 定义初始化platform bus

b -- 定义各种platform devices

c -- 注册各种platform devices

d -- 定义相关platform driver

e -- 注册相关platform driver

f  -- 操作相关设备

platform_driver_register驱动注册:

platform_device(设备)与platform_driver(驱动)在platform(虚拟总线)

设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定。

platform驱动的工作过程

  设备(或驱动)注册的时候,都会引发总线调用自己的match函数来寻找目前platform总线是否挂载有与该设备(或驱动)名字匹配的驱动(或设备),如果存在则将双方绑定;

  如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;

  如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。

http://image.huawei.com/tiny-lts/v1/images/ab0162629f1c4bbd4f93_570x628.png@900-0-90-f.png

http://image.huawei.com/tiny-lts/v1/images/2cb5f2629f1c4d7070b5_554x654.jpg@900-0-90-f.jpg

platform_device 结构体 VS platform_driver 结构体

设备(硬件部分):中断号,寄存器,DMA等

platform_device 结构体

struct platform_device {

    const char *name;       名字

    int id;

    bool id_auto;

    struct device dev;   硬件模块必须包含该结构体

    u32 num_resources;        资源个数

    struct resource *resource;         资源

    const struct platform_device_id    *id_entry;

    /* arch specific additions */

    struct pdev_archdata archdata;

};

驱动(软件部分)

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;  

};

驱动入口:

start_kernel();//kernel的第一个函数

rest_init();

kernel_init();

kernel_init_freeable();

do_basic_setup();

do_initcalls();//

do_initcall_level();

//......

module_init();

   设备信息存在设备树中,设备树加载的时候被转换成设备结构体。platform不在像以前那样匹配设备名字,而是匹配驱动中的.compatible与设备树中相应节点的compatible属性是否一致,且不区分大小写。一致则调用probe函数。

  probe函数被调用后,系统就调用platform设备的probe函数完成驱动注册最后工作。

driver到probe:

XXX_register_driver();

driver_register();

bus_add_driver();

driver_attach();

__driver_attach (for your device);

driver_probe_device();

really_probe();

XXX_device_probe (this is what dev->bus->probe is for an XXX driver);

your_probe_func();

 

关于probe函数,在里边主要做的主要工作如下:

a,给设备上电;

  主要用到的函数为:regulator_get()、 regulator_set_voltage()        

  具体上多少电,在哪一个电路上电,和你在dtsi文件的配置有关,而dtsi如何来配置,

  也取决于设备在硬件上接哪路电路供电以及芯片平台对配置选项的定义。

b,初始化设备;

c,创建相关节点接口;

  主要用到的函数为: sysfs_create_group();

  需要实现static struct attribute_group 的一个结构体以及你需要的接口

创建完节点后,就可以在linux应用层空间通过操作节点来调用驱动里边的对应函数了。

 

sysfs向用户空间展示了驱动设备的层次结构。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。

sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。

 kobject是一个对象的抽象,它用于管理对象。每个kobject对应着sysfs中的一个目录。

 kset是一些kobject的集合,这些kobject可以有相同的ktype,也可以不同。同时,kset自己也包含一个kobject。在sysfs中,kset也是对应这一个目录,但是目录下面包含着其他的kojbect。

每个kobject对象都内嵌有一个ktype,该结构定义了kobject在创建和删除时所采取的行为。

http://image.huawei.com/tiny-lts/v1/images/d882e2629f1c49805edf_427x306.png@900-0-90-f.png

 

注册sysfs文件系统:

start_kernel( ) ->  vfs_caches_init( ) ->  mnt_init( ) -> sysfs_init( )。

http://image.huawei.com/tiny-lts/v1/images/aa3c82629f1c4e6e71e9_502x558.png@900-0-90-f.png

 

 

Linux设备驱动的设计思想

 

 1、设备与驱动分离


2、驱动分层


3、设备驱动分层


 

Linux设备驱动所需能力:

·编写Linux设备驱动要求有非常好的硬件基础,懂得SRAM、Flash、SDRAM、磁盘的读写方式,UART、I2C、USB等设备的接口以及轮询、中断、DMA的原理,PCI总线的工作方式以及CPU的内存管理单元(MMU)等。

·编写Linux设备驱动要求有非常好的C语言基础,能灵活地运用C语言的结构体、指针、函数指针及内存动态申请和释放等。

·编写Linux设备驱动要求有一定的Linux内核基础,虽然并不要求工程师对内核各个部分有深入的研究,但至少要明白驱动与内核的接口。尤其是对于块设备、网络设备、Flash设备、串口设备等复杂设备,内核定义的驱动体系结构本身就非常复杂。

·编写Linux设备驱动要求有非常好的多任务并发控制和同步的基础,因为在驱动中会大量使用自旋锁、互斥、信号量、等待队列等并发与同步机制。

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值