1、预备知识:
应用程序、库、内核、驱动程序的关系
应用程序调用应用程序函数库完成功能
应用程序以文件形式访问各种资源
应用程序函数库
部分函数直接完成功能
部分函数通过系统调用由内核完成
内核处理系统调用,调用设备驱动程序
设备驱动直接与硬件通信
设备类型
字符设备
对字符设备发出读/写请求时,实际的硬件I/O操作一般紧接着发生
块设备
块设备与之相反,它利用系统内存作为缓冲区
网络设备
网络设备是一类特殊的设备,它不像字符设备或块设备那样通过对应的设备文件节点访问,也不能直接通过read或write进行数据访问请求
主设备号与从设备号
在设备管理中,除了设备类型外,内核还需要一对被称为主从设备号的参数,才能唯一标识一个设备
主设备号相同的设备使用相同的驱动程序
从设备号用于区分具体设备的实例
cat /proc/devices可以查看系统中所有设备对应的主设备号
设备文件
设备类型、主从设备号是内核与设备驱动程序通信时使用的
应用程序使用设备文件节点访问对应设备
每个主从设备号确定的设备都对应一个文件节点
每个设备文件都有其文件属性(c或者b)
每个设备文件都有2个设备号(主,从)
设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致
系统调用是内核与应用程序之间的接口
设备驱动程序是内核与硬件之间的接口
驱动程序与应用程序的区别
应用程序以main开始
驱动程序没有main,它以一个模块初始化函数作为入口
应用程序从头到尾执行一个任务
驱动程序完成初始化之后不再运行,等待系统调用
应用程序可以使用GLIBC等标准C函数库
驱动程序不能使用标准C库
用户态与内核态
驱动程序是内核的一部分,工作在内核态。 应用程序工作在用户态
数据空间访问问题
无法通过指针直接将二者的数据地址进行传递
系统提供一系列函数帮助完成数据空间转换 copy_from_user copy_to_user
Linux驱动程序功能
对设备初始化和释放
把数据从内核传送到硬件和从硬件读取数据
读取应用程序传送给设备文件的数据和回送应用程序请求的数据
检测和处理设备出现的错误
2、Linux字符设备驱动框架
file_operations结构体
规定了驱动程序向应用程序提供的操作接口
实现函数:
open 当应用程序打开设备时对设备进行初始化
release 关闭设备时处理关闭操作
read 从硬件读取数据并交给应用程序
write 从应用程序接收数据送到硬件
ioctl 为应用程序提供对硬件行为的控制
3、Linux字符设备驱动框架
Linux在加载内核模块时会调用初始化函数
static int __int XXXX_init(void)
使用register_chrdev向内核注册驱动程序
驱动退出:
Linux在卸载内核模块时会调用退出函数
static void __exit XXXX_exit(void)
使用unregister_chrdev从内核中卸载驱动程序
内核需要知道模块的初始化函数和退出函数,才能将模块放入自己的管理队列中
module_init(XXXX_init)
向内核声明当前模块的初始化函数
module_exit(XXXX_exit)
向内核声明当前模块的退出函数
4、中断处理
释放中断: free_irq()
禁止单个中断: disable_irq()
允许单个中断: enable_irq()
禁止所有中断: local_irq_disable()
允许所有中断: local_irq_enable()
5、同步机制
内核需要提供并发控制机制,对公共资源的访问进行同步控制,确保共享资源的安全访问。
Linux同步机制:自旋锁(spinlock),信号量(semaphore),读写锁(rwlock),
顺序锁(seqlock),RCU(Read-Copy Update)
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/device.h> #include <linux/gpio.h> #define DEVICE_NAME "mydriver" static int MYDRIVER_Major = 0;//为0,系统自动分配设备号。 static int mydriver_open(struct inode *inode, struct file *file) { printk("My Driver Open Called!\n"); return 0; } static int mydriver_release(struct inode *inode, struct file *file) { printk("My Driver Release Called!\n"); return 0; } static int mydriver_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { printk("My Driver Read Called!\n"); return 0; } static int mydriver_write(struct file *filp, char *buf, size_t count, loff_t *f_pos) { printk("My Driver Write Called!\n"); return 0; } static int mydriver_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { } static struct file_operations mydriver_fops = { .owner = THIS_MODULE, .open = mydriver_open, .release = mydriver_release, .read = mydriver_read, .write = mydriver_write, .ioctl = mydriver_ioctl, }; static struct class *mydriver_class; static int __init mydriver_init(void) { printk("MY DRIVER MODULE INIT\n"); MYDRIVER_Major = register_chrdev(0, DEVICE_NAME, &mydriver_fops); if (MYDRIVER_Major < 0) { printk(DEVICE_NAME " can't register major number\n"); return MYDRIVER_Major; } printk("register My Driver OK! Major = %d\n", MYDRIVER_Major); //注册一个类,使mdev可以在"/dev/"目录下面建立设备节点 mydriver_class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(mydriver_class)) { printk("Err: failed in My Driver class. \n"); return -1; } //创建一个设备节点,节点名为DEVICE_NAME device_create(mydriver_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME); printk(DEVICE_NAME " initialized\n"); return 0; } static void __exit mydriver_exit(void) { printk("MY DRIVER MODULE EXIT\n"); unregister_chrdev(MYDRIVER_Major, DEVICE_NAME); device_destroy(mydriver_class, MKDEV(MYDRIVER_Major, 0)); class_destroy(mydriver_class); } module_init(mydriver_init); module_exit(mydriver_exit); MODULE_AUTHOR("www.txmcu.com"); MODULE_DESCRIPTION("My Driver"); MODULE_LICENSE("GPL");测试程序模板:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> int main(int argc, char **argv) { int fd; fd = open("/dev/XXXX", 0); if (fd < 0) { perror("open device"); exit(1); } /*your code*/ close(fd); return 0; }