zoukankan      html  css  js  c++  java
  • Linux设备驱动开发流程(转)

    一、Linux设备的分类

     

    字符设备、块设备、网络设备,三种设备之间的区别是数据的交互模式,分别为:

    字节流、数据块、数据包。

     

    二、VFS核心结构体

     

    VFS核心结构体定义在"linux/fs.h"头文件中。

     

    1、struct inode结构体

    记录文件的属主、访问时间等信息。当第一次打开文件的时候由VFS创建并初始化。当文件的所有引用都退出后,释放inode; 如果用户态有多个人同时打开一个文件,则VFS只需要分配一个inode。

     

    2、struct file结构体

    对应用户态的open操作。如果多次打开同一个文件,内核会生成多个file。file中记录文件的打开方式,文件内部指针等。当文件彻底关闭时,释放file。

     

    3、struct file_operations结构体

    该结构体包含若干函数指针,这些函数由驱动来实现,并集中到file_operations中,注册到VFS。

    驱动一般要实现的函数有:

    open

    release

    read

    write

    unlocked_ioctl

    驱动可能会实现的有:

    poll

    mmap

    fasync

    flush

    llseek

     

    三、字符设备驱动开发流程

     

    (1)确定硬件信息

    要确定硬件的数量,物理地址,中断号等信息;

     

    (2)为要支持的设备准备一个私有结构体

    内核并不要求必须有私有结构体,但如果驱动支持多个设备,最好设计一个。私有结构体完全由驱动人员自行设计,一般来说,会把和设备相关的信息写入该结构体,比如设备的地址等。

     

    (3)为要支持的每个设备分配对应的设备号

    设备号由char驱动分配,要求唯一。一般来说,如果char驱动可支持多个类似的设备,则应该为这些设备选择一个主设备号,然后为每个设备选择一个特定的次设备号。尽量挑选和其他驱动不一样的主设备号。可以看/proc/devices,文件中记录了其他驱动选择的主设备号;也可以向内核申请,由内核分配一个主设备号。

     

    #define DEV_MAJOR 50

    ...

    dev_id = MKDEV(DEV_MAJOR, 0);

    (4)准备file_operations结构体

    函数集中包括open/release/read/write/unlocked_ioctl等函数,如果驱动支持多个设备,在函数中必须能区分自己访问的是哪个设备。

     

    static struct file_operations mem_fops = {

    .owner = THIS_MODULE,

    .open = mem_open,

    .release = mem_release,

    .read = mem_read,

    .write = mem_write,

    .unlocked_ioctl = mem_ioctl,

    };

    (5)注册设备

     

    方法一:

     

    利用cdev结构体,将设备号和file_operations注册到VFS。一般来说,将cdev结构体包含到私有结构体中。采用cdev注册的设备,不会自动创建设备文件。

     

    cdev_init(&mem_cdev, &mem_fops);

    cdev_add(&mem_cdev, dev_id, 1);

    cdev_del(&mem_cdev); //注销cdev

    方法二:

     

    注册miscdevice(用misc代替cdev注册,可以自动创建设备文件)

    这种情况下不需要定义主设备号,即省去#define DEV_MAJOR 50,同时需要用头文件"linux/miscdevice.h"代替"linux/cdev.h"头文件。

     

    static struct miscdevice mem_miscdev = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = "mem", //对应/dev/mem设备文件

    .fops = &mem_fops,

    };

     

    ret = misc_register(&mem_miscdev); //注册miscdevice

    misc_deregister(&mem_miscdev); //注销miscdevice

    cdev和miscdevice比较:

    (1)cdev可以实现同一个驱动对应多个设备,而miscdevice只能实现一个驱动对应一个设备;

    (2)cdev不能实现设备文件的自动创建,而miscdevice可以实现设备文件的自动创建。

     

    上述的过程比较适合较简单的设备,比如看门狗,led灯,各种传感器等。较复杂设备的char驱动,常常要利用内核提供的驱动子系统代码进行设计。

     

    四、如何在linux驱动中访问寄存器(SFR)

     

    1、片内外设(pripheral)

    (1)基于三总线访问

    (2)用寄存器控制

    (3)寄存器有物理地址,可通过手册查到,不可更改

     

    2、片外外设

    (1)很少通过三总线相连,一般是通过UART,CAN,I2C,USB,SPI,MIPI,I2S,AC97等总线相连。主芯片内部必须提供对应的控制器:内部的控制器 <–> 外部的外设

    (2)片外外设基本都是智能设备(不但有硬件,还有软件)

    (3)有些片外外设通过寄存器控制,有些则通过命令控制

    (4)如果用寄存器控制,寄存器没有物理地址,只有偏移。

    (5)要控制片外外设,需要首先了解对应的总线

     

    3、访问寄存器的流程

    由于linux使能了MMU,因此对于驱动来说,不能直接使用寄存器的物理地址,必须将其映射为虚拟地址才可以使用。

     

    (1)定义寄存器物理基地址以及寄存器的偏移

     

    #define GPIO_BASE 0x11000000

    #define GPIO_SIZE 0x1000 //0x8

    #define GPM4CON 0x2E0 //偏移地址

    #define GPM4DAT 0x2E4 //偏移地址

    GPIO_SIZE为寄存器的范围,可以按照使用的寄存器的总大小进行计算,比如用了两个寄存器,范围是0x8;但由于地址映射的最小单位是4K,因此小于4K的值都是可以的。

     

    (2)将寄存器物理地址映射到虚拟地址,如果映射不成功,则无法访问寄存器

     

    static void __iomem *vir_base;

    vir_base = ioremap(GPIO_BASE, GPIO_SIZE);

    if (!vir_base) {

    printk("Cannot ioremap ");

    return -EIO;

    }

    (3)访问寄存器,一般采用基地址加偏移的模式,内核根据寄存器的大小,提供了一系列函数

     

    8位寄存器的访问

     

    char value;

    value = readb(vir_base + offset);

    writeb(value, (vir_base + offset));

    16位寄存器的访问

     

    short value;

    value = readw(vir_base + offset);

    writew(value, (vir_base + offset));

    32位寄存器的访问

     

    int value;

    value = readl(vir_base + offset);

    writel(value, (vir_base + offset));

    64位寄存器的访问

     

    u64 value;

    value = readq(vir_base + offset);

    writeq(value, (vir_base + offset));

    (4)取消寄存器的映射

     

    iounmap(vir_base);

  • 相关阅读:
    如何选择Linux操作系统版本?
    js+html实现玫瑰花绽放
    Linux系统目录结构
    laravel5.6操作数据curd写法(查询构建器)
    laravel5.6 常规框架部署和配置文件说明
    PHP读取XML文件数据获取节点值
    Fiddler正则匹配调试接口示例
    php常用端口号
    php heredoc的用法详解
    oracle表空间操作
  • 原文地址:https://www.cnblogs.com/yangjiquan/p/11488661.html
Copyright © 2011-2022 走看看