1 Linux设备驱动概述及开发环境构建
1.1 设备驱动的作用
驱使硬件设备行动1.2 无操作系统时的设备驱动
典型架构:一个无限循环中夹杂着对设备中断的检测或者对设备的轮询1.3 有操作系统时的设备驱动
并发 、内存管理
1.4 Linux 设备驱动
1.4.1设备的分类及特点
● 字符设备。
● 块设备。
● 网络设备。
1.4.2 Linux 设备驱动与整个软硬件系统的关系
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 数字信号处理器
2.2 存储器
2.3 接口与总线
串口 、I2CI2C 、SPI 、USB、以太网 、PCI 和 PCI-E 、SD 和 SDIO
2.4 CPLD 和 FPGA
2.5原理图分析
符号 、网络 、描述2.6硬件时序分析
时序分析的意思是让芯片之间的访问满足芯片数据手册中时序图信号有效的先后顺序、采样建立时间(Setup Time)和保持时间(Hold Time)的要求2.7芯片数据手册阅读方法
2.8仪器仪表使用
万用表 、示波器 、逻辑分析仪3 Linux 内核及内核编程
3.1 Linux 内核的发展与演变
表 3.1 Linux 操作系统版本的历史及特点3.2 内核组件
1. 进程调度
2. 内存管理
3. 虚拟文件系统
4. 网络接口
5. 进程间通信
进程间通信支持进程之间的通信, Linux 支持进程间的多种通信机制,包含信号量、共享内存、消息队列、管道、 UNIX 域套接字等,这些机制可协助多个进程、多资源的互斥访问、进程间的同步和消息传递。在实际的 Linux 应用中,人们更多地趋向于使用 UNIX 域套接字,而不是 System V IPC 中的消息队列等机制。 Android 内核则新增了 Binder 进程间通信方式。4 内核模块
4.1 模块简介
insmod ./hello.kormmod hellolsmod/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 文件系统与设备文件
6 字符设备驱动
6.1 驱动结构
6.1.1 cdev结构体
//生成devMKDEV(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结构体
7 设备驱动中的并发控制
7.1 并发与竞态
临界区:访问共享资源的代码段互斥:中断屏蔽、原子操作、自旋锁、信号量、互斥体7.2 编译乱序和执行乱序
表 隔离指令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 位原子操作
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);
7.5.2 读写锁
7.5.3 顺序锁
读执行单元不会被写执行单元阻塞;但写执行单元进行写操作时,其他写执行单元就会自旋。7.5.4 读-复制-更新
RCU: Read-Copy-Update
7.6 信号量
7.7 互斥体
7.8 完成量
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;}
8.1.2 支持等待队列的globalfifo
8.2 轮询操作
8.2.1 轮询的概念与作用
9.2.3 信号的释放
异步通知结构体
struct xxx_dev{struct cdev cdev;...struct fasync_struct *async_queue;}
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);}
释放读信号
//xxx_writeif(dev->async_queue)kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
从异步通知列表删除filp
//xxx_releasexxx_fasync(-1, filp, 0);
9.4 Linux异步I/O
9.4.1 AIO
struct aiocb {int aio_fildes; // File Descriptorint aio_lio_opcode; // Valid only for lio_listio (r/w/nop)volatile void *aio_buf; // Data Buffersize_t aio_nbytes; // Number of Bytes in Data Bufferstruct sigevent aio_sigevent; // Notification Structure/* Internal fields */...};
9.4.2 内核AIO与libaio
10 中断与时钟
10.1 中断与定时器
11 内存与I/O访问
17 I2C、SPI、USB驱动架构类比
18 ARM Linux设备树
18.1 ARM设备树起源
可描述的信息:CPU的数量和类别内存基地址和大小总线和桥外设连接中断控制器和中断使用情况GPIO控制器和GPIO使用情况时钟控制器和时钟使用情况
18.2 设备树的组成和结构
18.2.1 DTS、DTC和DTB
.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 {};};};
.dtc:device tree compiler
.dtb:Device Tree Blob