Linux 内核中驱动程序是通过内核模块编码的形式插入到内核中
Linux内核将外设分为三类
1、字符设备:应用程序与该设备之间数据交互形式是“字节流”---一般只能顺序访问
2、快设备:应用程序与该设备之间数据交互以块为单位---一般支持随机访问
3、网络设备:应用程序与该设备之间数据交互建立在某种协议栈的基础上
Linux内核中采用struct cdev类型的对象来表示一个字符设备,因此内核中与多少字符设备驱动程序,就有多少个struct cdev类型的对象
Linux内核采用一种链式数据结构来管理这些struct cdev类型的对象
每一个struct cdev对象都有唯一的身份标识--------设备号
那么什么是设备号呢?
设备号是一个32位的整数(dev_t 类型),其中高12位被称为主设备号,低20位被称为次设备号。
内核提供了三个宏:
1、MKDEV(主设备号,次设备号) -----组合成完整的设备号
2、MAJOR(完整设备号) -------从完整设备号中分离主设备号
3、MINOR(完整设备号) -------从完整设备号中分离次设备号
主设备号:表示哪一类设备,同类设备的驱动程序逻辑是一样的
次设备号:表示该类设备中的哪一个设备。
编写字符设备驱动程序的基本步骤大体如下
insmod时:
1、创建一个struct cdev类型对象
(1)、全局变量定义
(2)、动态分配
2、(向内核申请)为该对象指定一个空闲的设备号(该设备号未被其他设备占用),作为struct cdeb对象的身份标识。
申请设备号:register_chrdev_region , alloc_chrdev_region
3、为struct cdev中的某些成员设置初值 cdev_init
4、将struct cdev 对象插入到内核管理该类对象的链式数据结构中 dev_add
rmmod时:
1、将本字符设备对应的struct cdev的设备号归还给内核 unregister_chrdev_region
2、从内核管理的struct cdev 对象的链式数据结构中移除本字符设备对应的struct cdev对象 cdev_del
3、如果struct cdev对象的空间是动态分配的,应释放其占用的内存空间
其中申请设备号可以手动分配也可以动态分配,内核提供了两个函数
int register_chrdev_region(dev_t from , unsigned count ,const char *name)
功能:手动分配设备号,先验证设备号是否被占用,若没有则申请占用该设备号
参数:from:自己指定的设备号
count:申请的设备数量
name:在/proc/devices文件中对于的设备名称,方便用户查询主设备号
(可通过cat /proc/devices命令查询)
返回值:成功0,失败负数
int alloc_chrdev_region(dev_t *dev ,unsigned baseminor , unsigned count , const char *name)
功能:动态分配设备号,查询内核里未被占用的设备号,如果找到则占用
参数:dev:分配设备号成功后用来存放分配的设备号
baseminior:起始的次设备号,一般为0
count:申请的设备数量
name :在/proc/devices文件中对于的设备名称,方便用户查询主设备号
返回值:成功0,失败负数
下面是具体的申请设备号的代码实现
#include <linux/module.h>
#include <linux/kernel.h>
int major = 0;//主设备号
int minor = 0;//次设备号
int devcnt = 1; //申请设备数量
struct cdev mydev;
static int __init hello_init(void)//入口函数
{
dev_t devno = MKDEV(major,minor);
int ret = 0 ;
ret = register_chdev_region(devno,devcnt,"myfirstdev");
if(ret)
{
ret=alloc_chdev_region(&devno,minor,devcnt,"myfirstdev");
if(ret)
{
printK("alloc_chdev_region is failed\n");
return -1;
}
major = MAJOR(devno);
}
return 0;
}
static void __exit hello_exit(void)//出口函数
{
dev_t devno = MKDEV(major,minor);
unregister_chrdev_region(devno,devcnt);
cdev_del(&mydev);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");