2015-08-10 14:42:59 21cnbao 阅读数 15710

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》


china-pub   天猫     dangdang   京东   


China-pub 8月新书销售榜



推荐序一

  技术日新月异,产业斗转星移,滚滚红尘,消逝的事物太多,新事物的诞生也更迅猛。众多新生事物如灿烂烟花,转瞬即逝。当我们仰望星空时,在浩如烟海的专业名词中寻找,赫然发现,Linux的生命力之旺盛顽强,斗志之昂扬雄壮,令人称奇。它正以摧枯拉朽之势迅速占领包括服务器、云计算、消费电子、工业控制、仪器仪表、导航娱乐等在内的众多应用领域,并逐步占据许多WINCE、VxWorks的传统嵌入式市场。

  Linux所及之处,所向披靡。这与Linux的社区式开发模式,迅速的迭代不无关系。Linux每2~3月更新一次版本,吸纳新的体系架构、芯片支持、驱动、内核优化和新特性,这使得Linux总是能够在第一时间内迎合用户的需求,快速地适应瞬息万变的市场。由Linux以及围绕着Linux进行产品研发的众多企业和爱好者构成了一个庞大的Linux生态圈。而本书,无疑给这个庞大的生态圈注入了养料。

  然而,养料的注入应该是持续不断的。至今,Linux内核的底层BSP、驱动框架和内核实现发生了许多变更,本书涵盖了这些新的变化,这将给予开发者更多新的帮助。内核的代码不断重构并最优化,而本书也无疑是一次重大的重构。

  生命不息,重构不止。

  周立功

  推荐序二

  在翻译了《Understanding the Linux Kernel》和《Linux Kernel Development》这两本书后,每当有读者询问如何学习Linux内核时,我都不敢贸然给出建议。如此庞大的内核,各个子系统之间的关系错综复杂,代码不断更新和迭代,到底该从何入手?你的出发点是哪里?你想去的彼岸又是哪里?相应的学习方法都不同。

  一旦踏入Linux内核领域,要精通Linux内核的精髓,几乎没有捷径可走。尽管通往山顶的路有无数条,但每条路上都布满荆棘,或许时间和毅力才是斩荆披棘的利器。

  从最初到现在,Linux内核的版本更新达上千个,代码规模不断增长,平均每个版本的新增代码有4万行左右。在源代码的10个主要子目录(arch、init、include、kernel、mm、IPC、fs、lib、net、drivers)中,驱动程序的代码量呈线性增长趋势。

  从软件工程角度来看内核代码的变化规律,Linux的体系结构相对稳定,子系统数变化不大,平均每个模块的复杂度呈下降趋势,但系统整体规模和复杂性分别呈超线性和接近线性增长趋势。drivers和arch等模块的快速变化是引起系统复杂性增加的主因。那么,在代码量最多的驱动程序中,有什么规律可循?最根本的又是什么?

  本书更多的是关于Linux内核代码背后机理的讲解,呈现给读者的是一种思考方法,让读者能够在思考中举一反三。尽管驱动程序只是内核的一个子系统,但Linux内核是一种整体结构,牵一发而动全局,对Linux内核其他相关知识的掌握是开发驱动的基础。本书的内容包括中断、定时器、进程生命周期、uevent、并发、编译乱序、执行乱序、等待队列、I/O模型、内存管理等,实例代码也被大幅重构。

  明代著名的思想家王明阳有句名言“知而不行,是为不知;行而不知,可以致知”。因此在研读本书时,你一定要亲身实践,在实践之后要提升思考,如此,你才可以越过代码本身而看到内核的深层机理。

  陈莉君

  西安邮电大学


媒体评论

  十多年前,我在海外一家路由器公司从事底层软件开发时,一本《Linux Device Driver》(LDD)使我受益匪浅。近年来,我在从事基于ARM的嵌入式操作系统教学时发现,很多Linux设备驱动中的新技术,比如Device Tree、sysfs等,在LDD3中均没有涉及。而市面上的翻译书晦涩难懂,有的还不如英文原书好理解。宋宝华是我尊敬的技术人员,十年如一日,在Linux内核及设备驱动领域潜心耕耘,堪称大师。本书无论从汉语的遣词造句,案例的深入浅出,还是对前沿技术的掌握,对难点技术丝丝入扣的分析,都体现出了强烈的“工匠精神”,堪称经典,值得推荐。
  ——Xilinx前大中华区大学计划经理、慕客信CEO  谢凯年


  设备驱动程序是连接计算机软件和硬件的纽带和桥梁,开发者在嵌入式操作系统的开发移植过程中,有将近70%~80%的精力都用在了驱动程序的开发与调试方面。这就对设备驱动程序开发人员提出了极高的要求。开发者不仅要同时具备软件和硬件的知识和经验,而且还要不断地学习、更新自己,以便跟上嵌入式系统日新月异的发展。研究前人的总结和动手实践是不断提高自己的有效途径。虽然市面上已经有多种设备驱动的书籍,但本书在总结Linux设备驱动程序方面仍然非常具有特色。它用理论联系实际,尤其是提供了大量的实例,对读者深入地理解并掌握各种驱动程序的编写大有裨益。
  ——飞思卡尔半导体(中国)有限公司数字网络软件技术方案部总监  杨欣欣博士


  一位优秀的设备驱动开发工程师需要具备多年嵌入式软件和硬件开发经验的积累,本书针对Linux设备驱动开发相关的设计思想、框架、内核,深入浅出,结合代码,从理论到实践进行重点讲解。毫无疑问,本书可谓一把通向设备驱动大师殿堂之门的金钥匙,它将激发你的味蕾,带你“品尝”嵌入式设备驱动开发这道“美味佳肴”,掩卷沉思,意味深长。
  ——ARM中国在线社区经理   宋斌


  作者长期从事嵌入式Linux开发和教学工作,擅长Linux驱动开发,并跟踪开源软件和嵌入式处理器技术的最新发展,撰写本书,书中内容新鲜实用。作者针对ARM和移动便携式设备的兴起,在书中添加了ARM Linux 设备树和Linux电源管理系统架构及驱动的内容,书中关于Linux设备驱动的软件架构思想的章节也很有特色。
  ——中国软件行业协会嵌入式系统分会副理事长 何小庆


封面:


2017-05-29 20:29:57 pdfebook 阅读数 917

本书介绍了Linux设备驱动开发理论、框架与实例,详细说明了自旋锁、信号量、完成量、中断顶/底半部、定时器、内存和I/O映射以及异步通知、阻塞I/O、非阻塞I/O等Linux设备驱动理论,以及字符设备、块设备、tty设备、I2c设备、LCD设备、音频设备、USB设备、网络设备、PCI设备等Linux设备驱动架构中各个复杂数据结构和函数的关系,并讲解了Linux驱动开发的大量实例,使读者能够独立开发各类Linux设备驱动。

 

【备注:本书第11页,第六行链接已经失效,替换为:http://pan.baidu.com/s/1cFrl2e 密码:ezs2】

作者简介

  宋宝华,Linux布道者,知名嵌入式系统专家,《Essential Linux Device Drivers》译者。作为较早从事Linux内核与设备驱动研究的专家之一,他在众多国内外知名企业开展Linux技术培训。他也是一位活跃的Linux开发者和深度实践者,为Linux官方内核贡献了大量的Linux源码并承担代码审核工作。至今已向Linux官方内核提交逾数万行代码和几百个补丁。他的《Linux设备驱动开发详解》系列书在嵌入式Linux开发者中有口皆碑,是众多Linux书籍中为数不多的畅销书。

 
 
限个人学习使用,不得用于商业用途,请在下载后24小时内删除。
备注:资源来自网络,如有不合理可私信我,秒删。
电子书 Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf 宋宝华 免费下载
https://page55.ctfile.com/fs/14299555-204231232
 
 

2018-05-19 08:50:53 sinat_37817094 阅读数 327
设备驱动最通俗的解释就是“驱使硬件设备行动”

(1)
无操作系统时的设备驱动

在没有操作系统的情况下,设备驱动的接口被直接提交给应用软件工程师,应用软件没有
跨越任何层次就直接访问设备驱动的接口。

(2)
有操作系统时的设备驱动

有操作系统把单一的“驱使硬件设备行动”变成了操作系统内与硬件交互的模块,它对外
呈现为操作系统的 API,不再给应用软件工程师直接提供接口。                                   

(3)
设备的分类及特点

计算机系统的硬件主要由 CPU、存储器和外设组成。CPU 内部就集成了存储器和外设适配器。譬如,相当多的ARM、PowerPC、MIPS 等处理器都集成了 UART、I 2 C 控制器、SPI 控制器、USB 控制器、SDRAM 控制器等,有的处理器还集成了 GPU(图形处理器)、视频编解码器等。
驱动针对的对象是存储器和外设(包括 CPU 内部集成的存储器和外设),而不是针对CPU 内核。
Linux 将存储器和外设分为 3 个基础大类:
字符设备:字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等
块设备:块设备可以按任意顺序进行访问,以块为单位进行操作,如硬盘、eMMC 等
网络设备:网络设备面向数据包的接收和发送而设计,使用套接字接口。


(4)
Linux设备驱动与整个软硬件系统的关系

除网络设备外,字符设备与块设备都被映射到 Linux 文件系统的文件和目录,通过文件系统的系统调用接口 open()、write()、read()、close() 等即可访问字符设备和块设备。/dev/所有字符设备和块设备都统一呈现给用户。

Linux 的块设备有两种访问方法:
1. 类似 dd 命令对应的原始块设备,如“ /dev/sdb1”等;
2. 在块设备上建立FAT、EXT4、BTRFS 等文件系统,然后以文件路径如“ /home/barry/hello.txt ”的形式进行访问。

在Linux中,针对 NOR、NAND 等提供了独立的内存技术设备(Memory TechnologyDevice,MTD)子系统,其上运行 YAFFS2、JFFS2、UBIFS 等具备擦除和负载均衡能力的文

件系统。针对磁盘或者Flash设备的 FAT、EXT4、YAFFS2、JFFS2、UBIFS 等文件系统定义了文件和目录在存储介质上的组织。而Linux的虚拟文件系统则统一对它们进行了抽象

(5)
Linux 设备驱动的重点、难点

Linux 设备驱动的学习是一项浩繁的工程,包含如下重点、难点:
1. 编写 Linux 设备驱动要求工程师有非常好的硬件基础,懂得 SRAM、Flash、SDRAM、磁盘的读写方式,UART、I 2 C、USB 等设备的接口以及轮询、中断、DMA 的原理,PCI 总线的工作方式以及 CPU 的内存管理单元(MMU)等。(外设和各个子系统,通信协议)

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

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

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


动手实践永远是学习任何软件开发的最好方法,阅读经典书籍和参与 Linux 社区的讨论也是非常好的学习方法。Linux 内核源代码中包含了一个 Documentation 目录,其中包含了一批内核设计文档,全部是文本文件。各类 Linux 设备驱动都从属于一个 Linux 设备驱动的架构,Linux 驱动的分析方法是点面结合,将对函数和数据结构的理解放在整体架构的背景之中。



(6)
设备驱动 Hello World:LED 驱动

6.1.无操作系统时的 LED 驱动
在 嵌 入 式 系 统 的 设 计 中,LED 一 般 直 接 由 CPU 的 GPIO(通 用 可 编 程 I/O) 口 控 制。
GPIO 一般由两组寄存器控制,即一组控制寄存器和一组数据寄存器。
控制寄存器可设置GPIO 口的工作方式为输入或者输出:
当引脚被设置为输出时,向数据寄存器的对应位写入 1和 0 会分别在引脚上产生高电平和低电平;
当引脚设置为输入时,读取数据寄存器的对应位可获得引脚上的电平为高或低

6.2 Linux下的LED驱动
在Linux下,可以使用字符设备驱动的框架来编写,内核中实际实现了一个提供 sysfs 节点的 GPIO LED驱动,位于drivers/leds/leds-gpio.c 中
字符设备注册、分配 和 注销的函数 register_chrdev_region()、alloc_chrdev_region()、unregister_chrdev_region()等
2018-04-04 19:16:47 limHappipen 阅读数 2861

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 、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

2019-04-09 16:38:06 qq_38131812 阅读数 242

区别于Linux4.0之前的字符设备驱动结构,4.0采用cdev注册字符设备。

一、构造一个字符设备结构体,用于cdev的初始化

struct led_dev_t{
	struct cdev cdev;
};

二、__init 入口函数

1.设备号的处理

dev_t led_devno= MKDEV(led_major,0);

MKDEV(led_major,0)通过主次设备号生成dev_t,在cdev的结构体里面定义了成员dev_t为,32位,12位是主设备号,20位是次设备号。如果不想自己设定主次设备号,可以使用MAJOR(dev_t dev)MINOR(dev_t dev)生成主次设备号。

if(led_major)
		{
			ret = register_chrdev_region(led_devno, 1,"led");
		}
	else
		{
			ret = alloc_chrdev_region(&led_devno, 0, 1, "led");
			led_major = MAJOR(led_devno);
		}

判断主设备号是否已定义,如果已定义,则注册设备号和设备名;否则申请一个设备号空间然后使用MAJOR(led_devno)生成主设备号。

2.注册字符设备前的准备:申请所需注册设备的结构体空间

ledevp = kzalloc(sizeof(led_dev_t), GFP_KERNEL);	/*申请内存空间*/
	
	/*判断内存是否申请成功*/
	if(!ledevp)
		{
			ret = -ENOMEM;
			unregister_chrdev_region(led_devno,1);
			return ret;
		}


3.注册字符设备

static void led_setup_cdev(struct led_dev_t *dev,int index)
{
	int err, led_devno = MKDEV(led_major,index);	/*通过主设备号和次设备号生成dev_t*/
	cdev_init(&dev->cdev,&chardev_led_fops);		/*初始化cdev*/
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev,led_devno,1);			/*添加一个cdev,param1:cdev,param2:主次设备号*/

	if(err)
		{
			printk(KERN_NOTICE"Error %d adding led %d",err,index);
		}

}

cdev_init初始化cdev,将本驱动的cdev添加进cdev链表

三、字符设备驱动模板

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_gpio.h>


#define LED_MAJOR 229

static int led_major = LED_MAJOR;

/*设备结构体*/
struct led_dev_t{
	struct cdev cdev;

};

struct led_dev_t *ledevp;

static int led_drv_open(struct inode *inode,struct file *file)
{
	/*设置LED引脚为输出模式*/
	
}
static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	/*向引脚写入值*/
	copy_from_user(..,buf,..);//从用户空间获得参数
}

const static struct file_operations chardev_led_fops = {
	.owner = THIS_MODULE,
	.open = led_drv_open,
	.write = led_drv_write
};


static void led_setup_cdev(struct led_dev_t *dev,int index)
{
	int err, led_devno = MKDEV(led_major,index);	/*通过主设备号和次设备号生成dev_t*/
	cdev_init(&dev->cdev,&chardev_led_fops);		/*初始化cdev*/
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev,led_devno,1);			/*添加一个cdev,param1:cdev,param2:主次设备号*/

	if(err)
		{
			printk(KERN_NOTICE"Error %d adding led %d",err,index);
		}

}
static int __init  chardev_led_init(void)
{
	int ret;
	dev_t led_devno= MKDEV(led_major,0);			

	/*获取字符设备号*/
	if(led_major)
		{
			ret = register_chrdev_region(led_devno, 1,"led");
		}
	else
		{
			ret = alloc_chrdev_region(&led_devno, 0, 1, "led");
		}
	
	ledevp = kzalloc(sizeof(led_dev_t), GFP_KERNEL);	/*申请内存空间*/
	
	/*判断内存是否申请成功*/
	if(!ledevp)
		{
			ret = -ENOMEM;
			unregister_chrdev_region(led_devno,1);
			return ret;
		}

	led_setup_cdev(ledevp,1);							/*设置字符设备*/
	return 0;
	
	
}


static void __exit chardev_led_exit(void)
{ 
	cdev_del(&ledevp->cdev);							/*删除cdev,释放内存空间*/
	kfree(ledevp);
	unregister_chrdev_region(MKDEV(led_major, 0), 1);
}

module_init(chardev_led_init);
module_exit(chardev_led_exit);
MODULE_AUTHOR(" MUGGLE <1198492751@qq.com>")

MODULE_LICENSE("GPL v2");



没有更多推荐了,返回首页