内核基本框架
应用层使用系统调用接口,进入内核子系统时间某种功能,内核子系统与硬件直接就是驱动。驱动根据需求不同会引用各个子系统。
驱动分为三种:字符设备驱动(硬件的读写以字节为单位)如串口、块设备驱动(读写是以块为单位)如硬盘,一个扇区为读写、网络设备驱动(通过协议 )can驱动,网卡驱动
驱动的静态加载与动态加载
区别:编译选项不同 (编译时选择,Y静态编译到内核中,选M动态编译为驱动)
存在位置 动态加载驱动在文件系统上,静态加载驱动uimage中
加载时间 动态加载等文件系统启动时手动用命令加载,静态在uboot启动时启动
动态加载 优势:热插拔、驱动调试、开机优化
字符设备驱动
字符设备驱动相关概念和结构
设备号 类似身份证。内核中通过类型dev_t来描述设备号。实质是unsigner int 32位整数,其高12位为主设备号,低20位为次设备号。例如同一类别的设备会有相同的主设备号,比如两个串口,主设备号会相同,次设备号不同。
次设备号可以自己设置然后向内核注册,然后用下面的函数注册,如果没被别的设备注册则返回成功。
int register_chrdev_region(dev_t from,unsigned count ,const char* name)
设备信息描述
struct cdev{
struct kobiect kobj; //设备管理机制,由内核决定
struct module *owner; //为了加载驱动 会给它赋值
const struct file_operations *ops; //一个函数集 链接下面的行为结构体
struct list_head list; //一个链表
dev_t dev; //设备号
unsigned int count; //支持的设备
}
设备行为的描述 设备的工作
这个结构体内大部分都是函数指针,这些函数用来操作硬件
struct file_operations{
struct module *owner;
...
ssize_t (*read)(struct file*,cha__user*,size_t,loff_t*)
ssize_t(*write)(struct file*,const char__user*,size_t,loof_t*)
int (*open)(struct inode*,struct file*)
int(*release)(struct inode*,struct file*)
...
}
相关注册函数 将设备注册到内核里面
int cdev_add(struct cdevp,dev_t dev,unsigned count) //cdevp 表示设备信息的结构体 第二个参数是设备号,第三个参数是设备个数
驱动源码分析
问题:
驱动代码的入口?(普通代码的入口是main函数)
驱动代码的生存周期?(什么时间加载到内存中,什么时候释放。一般应代码。执行完周期就结束)
当执行insmod时,驱动代码放入内存,生存周期开始。执行rmmod时,就会把驱动的声明周期结束
字符设备驱动的架构?
/*******************************************************
简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
*******************************************************/
#include<linux/module.h>
#include<linux/moduleparam.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/wait.h>
#include<poll.h>
#include<linux/sched.h>
#define BUFFER_MAX (10)
#define OK (0)
#define ERROR (-1)
struct cdev *gDev;
struct file_operations *gFile;
dev_t devNum;
unsigned int subDevNum = 1;
int reg_major =232;
int reg_minor = 0