zoukankan      html  css  js  c++  java
  • linux字符设备驱动--基本知识介绍

    一、设备驱动的分类

    1.字符设备

    字符设备是指那些能一个字节一个字节读取数据的设备,如LED灯、键盘、鼠标等。字符设备一般需要在驱动层实现open()、close()、read()、write()、ioctl()等函数。

    2.块设备

    块设备与字符设备类似,一般是像磁盘一样的设备。在块设备中还可以容纳文件系统,并存储大量的信息。在linux系统中,进行块设备读写时,每次只能传输一个或者多个块。linux也可以让应用程序像访问字符设备一样访问块设备,一次只读取一个字节。

    3.网络设备

    网络设备主要负责主机之间的数据交换,与字符设备和块设备完全不同,网络设备主要是面向数据包的接收和发送而设计的。网络设备没有实现类似块设备和字符设备的read()、write()、ioctl()等函数,但是实现了一种套接字接口,任何网络数据传输都可以通过套接字来完成。

    二、基础知识介绍

    1.直接将模块编译进内核(主要修改两个文件:Kconfig Makefile)

    1.1将编写好的模块程序复制到内核代码中(一般根据驱动内容来选择合适的路径)

    1.2进入到该路径,打开Kconfig文件:vi Kconfig

    说明:Kconfig文件描述了所属目录源文件相关的内核配置菜单

    1.3添加:config HELLO_WORLD

     bool "helloworld"

    则在menuconfig菜单中出现新的目录:helloworld

    1.4修改该目录下的Makefile文件:obj -$(CONFIG_HELLO_WORLD) += hello.o

    1.5编译内核

    三、字符设备驱动程序设计

    1.概念介绍

    1.1主次设备号

    主设备号用来标识与设备文件相连的驱动程序;次设备号被驱动程序用来辨别操作的是哪个设备。

    linux内核中通过dev_t类型描述主次设备号,dev_t其实质为unsigned int 32位整数,高12位为主设备号,低20位为次设备号。通过宏MAJOR(dev_t dev)获得主设备号,MINOR(dev_t dev)获得次设备号。

    1.2分配主设备号

    静态申请:确定一个系统没有使用的设备号,使用register_chrdev_region函数注册。但是该方式在驱动较多的情况下,很容易导致设备号冲突,而使驱动程序无法注册。

    动态分配:通过linux内核自动分配一个未使用的主设备号。使用alloc_chrdev_region()分配设备号。但是该方式无法在安装驱动前创建设备文件,因为安装前驱动程序还没有分配到主设备号。

    1.3创建设备文件

    使用mknod命令手动创建

    mknod用法:mknod filename type major minor

    filename:设备文件名

    type:设备文件类型(字符设备:c 块设备:b)

    major:主设备号

    minor:次设备号

    自动创建:

    2.重要结构

    2.1 struct file

    代表一个打开的文件,系统中每个打开的文件在内核空间都有一个关联的struct file,它是由内核在打开文件时创建,在文件关闭时释放。

    重要成员:loff_t f_pos /*文件读写位置*/

    struct file_operations *f_op

    2.2 struct inode

    用来记录文件的物理上的信息,因此它和代表打开文件的file结构是不同的,一个文件可以对应多个file结构,但只有一个inode结构。

    重要成员:dev_t i_rdev:设备号

    2.3 struct file_operations

    一个函数指针的集合,定义能在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。

    3.应用程序访问驱动程序

    当应用程序使用read函数时,传入要操作的文件file,系统会调用vfs_read函数,根据file去内核中查找相应的file_operations中指定的read函数,vfs_read函数(在read_write.c中)代码如下:

     1 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
     2 {
     3     ssize_t ret;
     4 
     5     if (!(file->f_mode & FMODE_READ))
     6         return -EBADF;
     7     if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read))
     8         return -EINVAL;
     9     if (unlikely(!access_ok(VERIFY_WRITE, buf, count)))
    10         return -EFAULT;
    11 
    12     ret = rw_verify_area(READ, file, pos, count);
    13     if (ret >= 0) {
    14         count = ret;
    15         if (file->f_op->read)
    16             ret = file->f_op->read(file, buf, count, pos);
    17         else
    18             ret = do_sync_read(file, buf, count, pos);
    19         if (ret > 0) {
    20             fsnotify_access(file);
    21             add_rchar(current, ret);
    22         }
    23         inc_syscr(current);
    24     }
    25 
    26     return ret;
    27 }
    View Code

    4.设备注册

    在linux2.6的内核中,字符设备使用struct cdev来描述。

    字符设备的注册过程可分为三步:1.分配cdev;2.初始化cdev;3.添加cdev;

    四、函数解析

    1.分配主设备号相关函数

    int register_chrdev_region(dev_t from,unsigned count,coust char *name)

    功能说明:申请使用从from开始的count个设备号(主设备号不变,次设备号增加)

    参数:from:希望申请使用的设备号

       count:希望申请使用的设备号数目

       name:设备号(体现在/proc/devices)

    int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)

    功能说明:请求内核动态分配count个设备号,且次设备号从baseminor开始。

    参数:dev:分配完返回的设备号

       baseminor:起始次设备号

       count:需要分配的设备号数目

       name:设备名(体现在/proc/devices)

    void unregister_chrdev_region(dev_t from,unsigned count)

    功能说明:释放从from开始的count个设备号

    参数:from:需要释放的设备号

       count:希望释放的设备号数目

    2.设备注册相关函数

    struct cdev *cdev_alloc(void)

    功能说明:分配cdev

    返回值:分配的cdev指针

    void cdev_init(struct cdev *cdev,const struct file_operations *fops)

    功能说明:初始化字符设备

    参数:cdev:待初始化的cdev结构

       fops:设备对应的操作函数集

    int cdev_add(struct cdev *p,dev_t dev,unsigned count)

    功能说明:添加字符设备

    参数:p:待添加到内核的字符设备结构

       dev:设备号

       count:添加的设备个数

    int cdev_del(struct cdev *p)

    功能说明:设备注销

    参数:p:要注销的字符设备结构

    3.设备操作函数

    int (*open)(struct inode *,struct file *)

    功能说明:在设备文件上的第一个操作,并不要求驱动程序一定要实现这个方法。如果该项为NULL,设备的打开操作永远成功。系统中通过fs/open.c中的do_sys_open函数实现。

    void (*release)(struct inode *,struct file *)

    功能说明:当设备文件被关闭时调用这个操作,与open相同,release也可以没有。

    ssize_t (*read)(struct file *,char __user *,size_t,loff_t *)

    功能说明:从设备中读取数据。

     

    ssize_t (*write)(struct file *,const char __user *,size_t,loff_t *)

     

    功能说明:向设备发送数据。

    unsigned int (*poll)(struct file *,struct poll_table_struct *)

    功能说明:对应select系统调用。

    int (*ioctl)(struct inode *,struct file *,unsigned int,unsigned long)

    功能说明:控制设备。

    int (*mmap)(struct file *,struct vm_area_struct *)

    功能说明:将设备映射到进程虚拟地址空间中。

    off_t (*Ilseek)(struct file *,loff_t,int)

    功能说明:修改文件的当前读写位置,并将新位置作为返回值。

     

     

     

    作者:Wcat
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    Linux常用技巧
    Python2 Python3 urllib方法对应
    Python常用技巧
    aix7安装was7、打补丁、更改访问端口、手动启动was、配置was7、部署项目
    微服务解释
    Java集合框架介绍。Java Collection Frameworks = JCF
    从今日起,坚持记录博客。
    java web 监控cpu、内存等。hyperic-sigar
    java线程中如何使用spring依赖注入
    java 下载文件 内容为空。
  • 原文地址:https://www.cnblogs.com/wcat/p/10105363.html
Copyright © 2011-2022 走看看