【Linux】Linux设备驱动开发详解:基于最新的Linux 4.0内核_linux 4.0 设备驱动-程序员宅基地

技术标签: Linux  驱动  

1 Linux设备驱动概述及开发环境构建

1.1 设备驱动的作用

  • 驱使硬件设备行动

1.2 无操作系统时的设备驱动

  • 典型架构:一个无限循环中夹杂着对设备中断的检测或者对设备的轮询
    无标题.png

1.3 有操作系统时的设备驱动

  • 并发 、内存管理

    无标题.png

1.4 Linux 设备驱动

1.4.1 设备的分类及特点

● 字符设备。
● 块设备。
● 网络设备。

1.4.2 Linux 设备驱动与整个软硬件系统的关系

捕获.PNG

1.4.3 Linux 设备驱动的重点、难点

● 编写 Linux 设备驱动要求工程师有非常好的硬件基础,懂得 SRAM、 Flash、 SDRAM、磁盘的读写方式,UART、 I2C、 USB 等设备的接口以及轮询、中断、 DMA 的原理,PCI 总线的工作方式以及 CPU 的内存管理单元(MMU)等。
● 编写 Linux 设备驱动要求工程师有非常好的 C 语言基础,能灵活地运用 C 语言的结构体、指针、函数指针及内存动态申请和释放等。
● 编写 Linux 设备驱动要求工程师有一定的 Linux 内核基础,虽然并不要求工程师对内核各个部分有深入的研究,但至少要明白驱动与内核的接口。尤其是对于块设备、网络设备、 Flash 设备、串口设备等复杂设备,内核定义的驱动体系结构本身就非常复杂。
● 编写 Linux 设备驱动要求工程师有非常好的多任务并发控制和同步的基础,因为在驱动中会大量使用自旋锁、互斥、信号量、等待队列等并发与同步机制。

2 驱动设计的硬件基础

2.1 处理器

2.1.1 通用处理器

2.1.2 数字信号处理器

捕获.PNG

2.2 存储器

捕获.PNG

捕获.PNG

2.3 接口与总线

串口 、 I2C I 2 C 、SPI 、USB、以太网 、PCI 和 PCI-E 、SD 和 SDIO

捕获.PNG

捕获.PNG

2.4 CPLD 和 FPGA

2.5 原理图分析

  • 符号 、网络 、描述

2.6 硬件时序分析

  • 时序分析的意思是让芯片之间的访问满足芯片数据手册中时序图信号有效的先后顺序、采样建立时间(Setup Time)和保持时间(Hold Time)的要求

2.7 芯片数据手册阅读方法

2.8 仪器仪表使用

  • 万用表 、示波器 、逻辑分析仪

3 Linux 内核及内核编程

3.1 Linux 内核的发展与演变

  • 表 3.1 Linux 操作系统版本的历史及特点
版 本 时 间 特 点
Linux 0.1 1991 年 10 月 最初的原型
Linux 1.0 1994 年 3 月 包含了 386 的官方支持,仅支持单 CPU 系统
Linux 1.2 1995 年 3 月 第一个包含多平台(Alpha、 Sparc、 MIPS 等)支持的官方版本
Linux 2.0 1996 年 6 月 包含很多新的平台支持,最重要的是,它是第一个支持 SMP(对称多处理器)体系的内核版本
Linux 2.2 1999 年 1 月 极大提升 SMP 系统上 Linux 的性能,并支持更多的硬件
Linux 2.4 2001 年 1 月 进一步提升了 SMP 系统的扩展性,同时也集成了很多用于支持桌面系统的特性: USB、 PC 卡(PCMCIA)的支持,内置的即插即用等
Linux 2.6.0 ~ 2.6.39 2003 年 12 月~2011 年 5 月 无论是对于企业服务器还是对于嵌入式系统, Linux 2.6 都是一个巨大的进步。对高端机器来说,新特性针对的是性能改进、可扩展性、吞吐率,以及对 SMP 机器 NUMA 的支持。对于嵌入式领域,添加了新的体系结构和处理器类型。包括对那些没有硬件控制的内存管理方案的无MMU 系统的支持。同样,为了满足桌面用户群的需要,添加了一整套新的音频和多媒体驱动程序
Linux 3.0 ~ 3.19、Linux 4.0-rc1 至今 2011 年 7 月至今 性能优化等 开发热点聚焦于虚拟化、新文件系统、 Android、新体系结构支持以及

3.2 内核组件

捕获.PNG

1. 进程调度

捕获.PNG

2. 内存管理

捕获.PNG

3. 虚拟文件系统

捕获.PNG

4. 网络接口

捕获.PNG

5. 进程间通信

  • 进程间通信支持进程之间的通信, Linux 支持进程间的多种通信机制,包含信号量、共享内存、消息队列、管道、 UNIX 域套接字等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递。在实际的 Linux 应用中,人们更多地趋向于使用 UNIX 域套接字,而不是 System V IPC 中的消息队列等机制。 Android 内核则新增了 Binder 进程间通信方式。

4 内核模块

4.1 模块简介

insmod ./hello.ko
rmmod hello

lsmod
/proc/modules
/sys/module

4.2 模块结构

4.2.1 加载函数

static int __init hello_init(void)
{
    ...

    return 0;
}

module_init(hello_init);

4.2.2 卸载函数

static void __exit hello_exit(void)
{
    ...
}

module_exit(hello_exit);

4.2.3 许可声明

MODULE_AUTHOR("lin");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("A simple param Module");
MODULE_ALIAS("a simplest module");
  • 模块参数module_param(var, int, S_IRUGO);
  • 导出符号EXPORT_SYMBOL_GPL(func); (proc/kallsyms)

5 文件系统与设备文件

捕获.PNG

捕获.PNG

6 字符设备驱动

6.1 驱动结构

6.1.1 cdev结构体

捕获.PNG

//生成dev
MKDEV(int major, int minor);    //major:0-19 minor:20-31
//获取设备号
MAJOR(dev_t dev)
MINOR(dev_t dev)
//cdev操作
void cdev_init(struct cdev *, struct file_operations *);
struct cdev* cdev_alloc(void);
void cdev_put(struct cdev *);
int  cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

6.1.2 设备号分配

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

int unregister_chrdev_region(dev_t from, unsigned count);

6.1.3 file_operations结构体

捕获.PNG

7 设备驱动中的并发控制

7.1 并发与竞态

  • 临界区:访问共享资源的代码段
  • 互斥:中断屏蔽、原子操作、自旋锁、信号量、互斥体

7.2 编译乱序和执行乱序

  • 表 隔离指令
指令名 功能描述
DMB 数据存储器隔离。DMB 指令保证: 仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。
DSB 数据同步隔离。比 DMB 严格: 仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访 问操作——译者注)
ISB 指令同步隔离。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。

7.3 中断屏蔽

local_irq_disable() local_irq_enable() //与自旋锁联合使用
local_irq_save(flags) local_irq_restore(flags)
local_bh_disable() local_bh_enable()

7.4 原子操作

7.4.1 整型原子操作

  • 设置

    void atomic_set(atomic_t *v, int i);
    atomic_t ATOMIC_INIT(int i);
  • 获取

    int atomic_read(atomic_t *v);
  • 加减

    void atomic_add(int i, atomic_t *v);
    void atomic_sub(int i, atomic_t *v);
    
    void atomic_inc(atomic_t *v);
    void atomic_dec(atomic_t *v);
  • 操作后测试(为0返回true,非0返回false)

    int atomic_inc_and_test(atomic_t *v);
    int atomic_dec_and_test(atomic_t *v);
    int atomic_sub_and_test(int i, atomic_t *v);
  • 操作后返回新值

    int atomic_add_return(int i, atomic_t *v);
    int atomic_sub_return(int i, atomic_t *v);
    
    int atomic_inc_return(atomic_t *v);
    int atomic_dec_return(atomic_t *v);

7.4.2 位原子操作

捕获.PNG

7.5 自旋锁

7.5.1 自旋锁

spinlock_t lock;
spin_lock_init(lock);
spin_lock(lock);
spin_trylock(lock);
spin_unlock(lock);


spin_lock_irq(lock); spin_unlock_irq(lock);
spin_lock__irqsave(lock); spin_unlock_irqrestore(lock);
spin_lock_bh(lock); spin_unlock_bh(lock);

无标题.png

7.5.2 读写锁

无标题.png

7.5.3 顺序锁

  • 读执行单元不会被写执行单元阻塞;但写执行单元进行写操作时,其他写执行单元就会自旋。

无标题.png

7.5.4 读-复制-更新

  • RCU: Read-Copy-Update

    捕获.PNG

    无标题.png

    7.6 信号量

    无标题.png

    7.7 互斥体

    无标题.png

    7.8 完成量

    无标题.png

8 阻塞I/O和非阻塞I/O

8.1 阻塞I/O和非阻塞I/O

fd= open("/dev/ttyS1", O_RDWR | O_NONBLOCK);
fcntl(fd, F_SETFL, O_NONBLOCK);

8.1.1 等待队列

//定义
wait_queue_head_t queue_head;
//初始化
init_waitqueue_head(&queue_head);
//定义及初始化
DECLARE_WAIT_QUEUE_HEAD(name)
//队列等待元素
DECLARE_WAITQUEUE(name, tsk)
//操作
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
//等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
//唤醒队列
void wake_up(wait_queue_head_t *q);
void wake_up_interruptible(wait_queue_head_t *q);
//睡眠
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
static ssize_t xxx_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
    ...
    DECLARE_WAITQUEUE(wait, current);
    add_wait_queue(&xxx_wait, &wait);

    /*等待设备缓冲区可写*/
    do {
        avail = device_writable();
        if (avail < 0) {
            if (file->f_flags & O_NONBLOCK) {
                ret = -EAGAIN;
                goto out;
            }
            __set_current_state(TASK_INTERRUPTIBLE);
            schedule();
            if (signal_pending(current)) {
                ret = -ERESTARTSYS;
                goto out;
            }
        }
    } while (avail < 0);

    device_write();
out:
    remove_wait_queue(&xxx_wait, &wait);
    set_current_state(TASK_RUNNING);

    reutrn ret;
}

捕获.PNG

8.1.2 支持等待队列的globalfifo

无标题.png

8.2 轮询操作

8.2.1 轮询的概念与作用

9.2.3 信号的释放

  1. 异步通知结构体

    struct xxx_dev{
        struct cdev cdev;
        ...
        struct fasync_struct *async_queue;
    }
    1. xxx_fasync
    static int xxx_fasync(int fd, struct file *filp, int mode)
    {
        struct xxx_dev *dev=file->private_data;
        return fasync_helper(fd, filp, mode, &dev->async_queue);
    }
    1. 释放读信号
    //xxx_write
    if(dev->async_queue)
      kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
    1. 从异步通知列表删除filp
    //xxx_release
    xxx_fasync(-1, filp, 0);

9.4 Linux异步I/O

9.4.1 AIO

struct aiocb {
 
  int aio_fildes;               // File Descriptor
  int aio_lio_opcode;           // Valid only for lio_listio (r/w/nop)
  volatile void *aio_buf;       // Data Buffer
  size_t aio_nbytes;            // Number of Bytes in Data Buffer
  struct sigevent aio_sigevent; // Notification Structure
 
  /* Internal fields */
  ...
 
};
API 函数 说明
aio_read int aio_read( struct aiocb *aiocbp ); 请求异步读操作
aio_error int aio_error( struct aiocb *aiocbp ); 检查异步请求的状态
aio_return ssize_t aio_return( struct aiocb *aiocbp ); 获得完成的异步请求的返回状态
aio_write int aio_write( struct aiocb *aiocbp ); 请求异步写操作
aio_suspend int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout ); 挂起调用进程,直到一个或多个异步请求已经完成(或失败)
aio_cancel int aio_cancel( int fd, struct aiocb *aiocbp ); 取消异步 I/O 请求
lio_listio int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig ); 发起一系列 I/O 操作

9.4.2 内核AIO与libaio

10 中断与时钟

10.1 中断与定时器

11 内存与I/O访问

17 I2C、SPI、USB驱动架构类比

无标题.png

18 ARM Linux设备树

18.1 ARM设备树起源

  • 可描述的信息:
    • CPU的数量和类别
    • 内存基地址和大小
    • 总线和桥
    • 外设连接
    • 中断控制器和中断使用情况
    • GPIO控制器和GPIO使用情况
    • 时钟控制器和时钟使用情况

18.2 设备树的组成和结构

18.2.1 DTS、DTC和DTB

  1. .dts:device tree source

    1.1 Soc共用部分:.dtsi (/include/ “s3c24440.dtsi”)

    1.2 模板

    /* root节点 */
    / {
        node1 {
            a-string-property = "A string";
            a-string-list-property = "first string", "second string";
            a-byte-data-property = [0x01 0x23 0x34 0x56];
            child-node1 {
                first-child-property;
                second-child-property = <1>;
                a-string-property = "Hello, world";
            };
            child-node2 {
            };
        };
        node2 {
            an-empty-property;
            a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
            child-node1 {
            };
        };
    };
  2. .dtc:device tree compiler

  3. .dtb:Device Tree Blob

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/limHappipen/article/details/79821307

智能推荐

视频教程-跟一夫学UI设计 APPUI综合设计与图标实战案例视频教程 photoshop绘制icon案例-UI-程序员宅基地

文章浏览阅读104次。跟一夫学UI设计 APPUI综合设计与图标实战案例视频教程 photoshop绘制icon案例 ..._app风格案例视频

vue 海康视频播放_vue-hkvideo-程序员宅基地

文章浏览阅读4k次,点赞4次,收藏22次。1. 下载并安装海康 web 插件https://open.hikvision.com/download/5c67f1e2f05948198c909700?type=102. 把上一步解压的三个 js, 复制到你的项目中, 根据路径, 自己引入到 index.html 中3. 建议运行它的 demo, 大概看看代码, 了解一下它的大致结构, 它的注解很详细, 3 分钟就能看完4. 贴上我的代码(我的是每次只显示一个画面, 点击摄像头切换画面)<temp.._vue-hkvideo

html li 鼠标经过变色,CSS实现li标签鼠标经过时改变背景颜色-程序员宅基地

文章浏览阅读5.3k次,点赞3次,收藏4次。很多时候需要用到这个css效果,实际上就用了一个li标签的热点样式,不仅是li标签,div等也可以的完整代码如下,div/css鼠标热点改变li标签背景颜色body{ background-color:#CCCC99; margin:0; padding:0; color:#fff;}ul{ margin:0; padding:50px;}li{ list-style:none; height:2..._ul li 样式 鼠标移入颜色

数据恢复:在 Linux 上恢复删除了的文件_linux系统,删了某一个文件夹的数据还清空了回收站,还能不能找回来我的数据-程序员宅基地

文章浏览阅读238次,点赞4次,收藏8次。把删除创建为rm -i 的别名当 -i 选项配合 rm 命令(也包括其他文件处理命令比如 cp 或者 mv)使用时,在删除文件前会出现一个提示。其中,/home/gacanepa/rescued 是另外一个磁盘中的目录 - 请记住,把文件恢复到被删除文件所在的磁盘中不是一个明智的做法。安装完成后,我们做一个简单的测试吧。如果在恢复过程中,占用了被删除文件之前所在的磁盘分区,就可能无法恢复文件。但愿你对于你的文件足够小心,当你要从外部磁盘或 USB 设备中恢复丢失的文件时,你只需使用这个工具即可。

2021-09-15 WPF上位机 15-属性绑定(数据格式化)_wpf 自定义属性绑定 格式化 实现-程序员宅基地

文章浏览阅读3.2w次。<Window x:Class="Zhaoxi.BindingStudy.DataFormatStudy.DataFormatStudyWin" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.._wpf 自定义属性绑定 格式化 实现

[常用办公软件] wps怎么自动生成目录?wps自动生成目录的设置教程_wps目录自动生成-程序员宅基地

文章浏览阅读1.1w次,点赞3次,收藏5次。转载请说明来源于"厦门SEO"本文地址:http://www.96096.cc/Article/160880.html常用办公软件  WPS Office是由金山软件股份有限公司开发的一款针对个人永久免费的办公软件,在我们的日常生活和工作中,WPS Office比起微软Microsoft Office来说在文字上的处理会更深入国人用户的人心,熟悉操作WPS的办公小技巧,能够更高效的提高我们的工作效率,今天小编要为大家分享的是WPS怎么自动生成目录?快来一起看看WPS自动生成目录的设置教程吧。_wps目录自动生成

随便推点

使用OkHttp 缓存 API 调用提高Android应用性能

要能够将 API 调用的响应本地存储到缓存中,首先,我们需要定义缓存并通知客户端。在下面的代码片段中,我们使用 okhttp 库中的 Cache 类定义了缓存。我们将此缓存的最大大小设置为 5 MB。然后,在初始化 okhttpclient 参数时使用cache()函数。.build()如果设备连接到互联网:如果最后一次 API 响应是在不到 30 分钟之前检索的,则显示缓存的响应;否则,获取新的响应并将其存储在缓存中。如果设备离线:使用最多 1 天前的 API 响应以保持应用程序功能。

一键实现在VS Code中绘制流程图

而其较为出众的一点,就是较好的可拓展性,即丰富的插件应用,这些应用可以极大地提高生产效率,并优化日常使用。可以发现,其整体格局和我们常见的流程图编辑应用较为类似,其主题颜色也与我们的VS Code保持一致,在这里为了编辑方便,我们还是将编辑器主题改为浅色。当然,其功能仍存在局限,不能够完全代替我们传统的图形绘制工具,但也可以作为我们日常工作的有益补充,帮助我们完成一些特定情景下的项目。整体布局也十分明晰,与我们常用的Visio极为类似:左侧为形状选项卡,中间为画布容器,右侧为样式编辑。

go http框架下的静态资源代理实现(压缩,缓存验证自定义)

之前在说了我的第一版静态资源代理,后面我又完善了一下:照着以上思路,可以在其他语言其他框架中实现,因为对框架没有依赖,都是使用的一些基本功能。

RecyclerView实现吸顶效果项目实战(三):布局管理器LayoutManager-程序员宅基地

文章浏览阅读338次,点赞4次,收藏6次。架构师不是天生的,是在项目中磨练起来的,所以,我们学了技术就需要结合项目进行实战训练,那么在Android里面最常用的架构无外乎 MVC,MVP,MVVM,但是这些思想如果和模块化,层次化,组件化混和在一起,那就不是一件那么简单的事了,我们需要一个真正身经百战的架构师才能讲解透彻其中蕴含的深理。此时,RecyclerView第一个item是添加进Adapter中的最后一个,最后一个item是第一个加进Adapter的数据,RecyclerView会自动滑到末尾,另外item整体是依靠下方的。

【智能排班系统】基于AOP和自定义注解实现接口幂等性-程序员宅基地

文章浏览阅读884次。使用多种方式实现接口幂等性,通过定义注解方便对方法进行幂等性控制

SpringBoot整合Swagger2 详解_springboot swagger2 开关-程序员宅基地

文章浏览阅读324次。SpringBoot、Swagger2 整合详解_springboot swagger2 开关