驱动学习
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f785a00113280b3b897533637ddd94ae.png)
每个设备都会有对应的设备节点(也就是对应文件/dev/xxx),内核区分设备是根据设备号而非设备节点,就好比每个人对应一个身份证号码而不是名字。
1.字符设备驱动框架
作为字符设备驱动要素:
- 必须有一个设备号,用在众多的设备驱动中进行区分
- 用户必须知道设备驱动对应的设备节点(设备文件)
Linux把所有的设备都看成文件,因为是通过文件IO形式操作(open()、read()、write()、close()等)
crw-r----- 1 root root 13, 64 Mar 28 20:14 event0
crw-r----- 1 root root 13, 65 Mar 28 20:14 event1
crw-r----- 1 root root 13, 66 Mar 28 20:14 event2 - 对设备操作其实就是对文件操作,应用空间操作open,read,write的时候,实际上是在驱动代码当中也有对应到open,read,write等操作。所以,例如,虽然open可以操作很多设备,但如果对应到/dev/设备节点,那么就实现了唯一性。对应/dev/led打开灯设备,对应/dev/key打开按键设备,……
2.申请设备号
a.作为驱动必须有一个设备号–向系统申请设备号
用于模块加载的函数中
int register_chrdev(unsinged int major, const char * name, const struct file_operations * fops)
参数1:主设备号(major)次设备号(minor)
设备号(32bit)==主设备号(12bit)+次设备号(20bit)
主设备号:表示一类设备–vcs
次设备号:表示同类设备中的某一个:vcs、vcs1、vcs2、……
$ls dev/ -l
以下的块设备也是这样
给定的方式有两种:
- 动态–参数1直接填0
- 静态–指定一个整数,比如123
参数2:描述一个设备信息(name),可以自定义
/proc/devices列举出所有的已注册设备
参数3:文件操作对象–提供open,read,write
返回值:正确返回0(return 0;),错误返回负数(return -EINVAL;)
b.作为驱动必须有一个设备号–系统释放设备号资源
用于模块卸载的函数中
unregister_chrdev(unsinged int major, const char * name)
参数1:主设备号(major)
参数2:描述一个设备信息,可以自定义(name)
c.修改Makefile
else
obj-m += [name].o
endif
先
m
a
k
e
c
l
e
a
n
然
后
重
新
make clean然后重新
makeclean然后重新make一下
cat /proc/devices查看用了什么设备
3.创建设备节点
a.手动创建
mknod /dev/设备名 类型 主设备号 次设备号
比如:
mknod /dev/chr0 c 123 0
# ls /dev/chr0 -l
crw-r--r-- 1 0 0 123, 0 Jan 1 00:33 /dev/chr0
缺点:/dev/目录中的文件都是在内存中,断电后/dev/文件就会消失
b.自动创建(通过udev/mdev机制)
- 先创建一个类
- 然后创建类里的设备
struct class *class_create(owner, name)//创建一个类
参数1:THIS_MODULE
参数2:字符串名字,自定义
返回一个class指针
//创建一个设备文件
struct device *device_create(struct class *class, struct device * parent, dev_t devt, void * drvdata, const char * fmt,…)
参数1:class结构体,class_create调用之后的返回值
参数2:表示父亲,一般直接填NULL
参数3:设备号类型 dev_t
设备号(32bit)==主设备号(12bit)+次设备号(20bit)
dev_t devt
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) ((ma) << MINORBITS) | (mi))
参数4:私有数据,一般直接填NULL
参数5、6:表示可变参数,字符串,表示设备节点的名字
有创建就有销毁,但顺序是:
3. 先释放设备资源
4. 然后再释放类资源
销毁动作:
void device_destroy(devcls, MKDEV(dev_major, 0));
参数1:class结构体,device_create调用之后的返回值
参数2:设备号类型 dev_t
void class_destroy(devcls);
参数:class结构体,class_create调用之后的返回值
最后实验成功如下:
4.驱动led灯