一、驱动分类
Linux中包含三大类驱动:字符设备驱动、块设备驱动和网路设备驱动。其中字符设备驱动是最大的一类驱动,由于字符设备最多,从led到I2C、SPI、音频等都属于字符设备驱动。块设备驱动和网路设备驱动都要比字符设备驱动复杂。由于其比较复杂,所以半导体厂商都早已帮我们写好了,大部份情况下都是可以直接使用的。所谓的块设备驱动就是储存器设备的驱动,例如EMMC、NAND、SD卡和U盘等储存设备
二、字符设备驱动开发
字符设备驱动是Linux驱动中最基本的一类设备驱动,字符设备就是一个一个字节,根据字节流进行读写操作的设备,读写数据是分先后次序的。我们先来了解Linux下的应用程序是怎样调用驱动程序的,如右图:
应用程序运行在用户空间,而Linux驱动属于内核的一部份,因而驱动运行于内核空间。open、close、write和read等这种函数是由C库提供的,在LInux中,系统调用作为C库的一部份。
三、Linux设备号1、设备好的组成
为了便捷管理,Linux中每位设备都有一个设备号red hat linux下载,设备号由主设备号和次设备号两部份组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备。Linux提供了一个名为dev_t的数据类型表示设备号,dev_t定义在include/linux/types.h上面。dev_t似乎是unsignedint类型,是一个32位的数据类型。其中高12位是主设备号,低20位是次设备号,因而Linux系统中主设备号范围是0~4095
设备号的操作函数
MAJOR用于从dev_t中获取主设备号,将dev_t右移20位即可
MINOR用于从dev_t中获取次设备号,取dev_t的低20位即可
MKDEV用于将给定的主设备号和次设备号的值组合成dev_t类型的设备号2、设备号的分配
静态设备号:注册字符设备须要给设备指定一个设备号,这个设备号可以是驱动开发者静态指定的设备号,然而要注意该设备号没有被内核开发者分配掉。使用cat/proc/devices即可查看当前系统中早已使用的设备号
使用register_chrdev函数注册字符设备的时侯只须要给定一个主设备号即可
/* 注册字符设备驱动 */
retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
这是老版本字符设备注册函数,其0~1048575(2^20-1)这个区间的次设备号全部设置为0
动态分配设备号:使用设备号的时侯向linux内核申请linux设备驱动开发详解 第三版,须要几个就申请几个,由linux内核分配设备可以使用的设备号。在注册字符设备之前先申请一个设备号,系统会手动给你一个没有被使用的设备号,这样就防止了冲突,卸载驱动的时侯释放掉这个设备号即可
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
dev:保存申请到的设备号
baseminor:次设备号起始地址,alloc_chrdev_region可以申请一段连续的多个设备号,这种设备好的主设备号一样,而且次设备号不同,次设备号以baseminor为起始地址开始递增。通常baseminor为0,也就是次设备号从0开始
count:要申请的设备号数目
name:设别名子
倘若给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号
intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)
参数from是要申请的起始设备号,也就是给定的设备号
参数count是要申请的数目,通常都是一个
参数name是设备名子
通常采用动态分配设备号,模板如下
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (newchrled.major)
{
/* 定义了设备号 */
newchrled.devid = MKDEV(newchrled.major, 0);
register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
}
else
{
/* 没有定义设备号 */
alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME); /* 申请设备号 */
newchrled.major = MAJOR(newchrled.devid); /* 获取分配号的主设备号 */
newchrled.minor = MINOR(newchrled.devid); /* 获取分配号的次设备号 */
}
printk("newcheled major=%d,minor=%drn",newchrled.major, newchrled.minor);
注销设备号
void unregister_chrdev_region(dev_t from, unsigned count)
四、模块注册与卸载
Linux驱动有两种运行方法,第一种是将驱动编译进Linux内核中,当Linux内核启动的时侯才会手动运行驱动程序。第二种是将驱动编撰成模块(Linux下模块扩充名为ko),在Linux内核启动之后使用相应加载驱动模块
模块有加载和卸载两种操作arm linux,模块的加载和卸载注册函数如下
module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数
module_init函数来向Linux内核注册一个模块加载函数,参数xxx_init就是须要注册的具体函数,当使用insmod加载驱动的时侯,xxx_init这个函数才会被调用
module_exit函数拿来向Linux内核注册一个模块卸载函数,参数xxx_exit就是须要注册的函数,当使用rmmod命令卸载驱动的时侯xxx_exit才会被调用
五、字符设备注册与注销
当驱动模块加载成功须要注册字符设备,卸载驱动模块的时侯也须要注销掉字符设备
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)
register_chrdev函数用于注册字符设备,unregister_chrdev注销掉字符设备
通常字符设备的注册在驱动模块的入口函数xxx_init中进行,字符设备的注销在驱动模块的出口函数xxx_exit进行
六、LICENSE和作者信息
在驱动中加如LICENSE和作者信息,其中LICENSE是必须添加的,否则的话编译会报错,作者信息可以不添加
MODULE_LICENSE("GPL") //添加模块 LICENSE 信息 ,LICENSE 采用 GPL 协议
MODULE_AUTHOR("mingfei") //添加模块作者信息
七、测试指令1、加载驱动模块
驱动模块通常挂载在lib/modules/4.1.15目录下,所以须要编撰的驱动模块和测试软件复制到根文件系统rootfs/lib/modules/4.1.15目录下,通过tftfp和nfs启动后,在开发板的lib/modules/4.1.15目录下存在驱动模块和测试软件
驱动编译完成之后扩充名是.kolinux设备驱动开发详解 第三版,有两种命令可以加载模块:insmod和modprobe
加载驱动模块
insmodxxx.ko
modprobexxx.ko
insmod不能解决模块的依赖关系,例如drv.ko依赖first.ko这个模块,就必须先使用insmod命令加载first.ko这个模块,之后再加载drv.ko这个模块
然而modprobe就不会存在这样的问题,modprobe会剖析模块的依赖关系,之后会将所有的依赖模块都加载到内核中
若modprobe提示未能打开modules.dep,驱动挂载失败,输入depmod即可手动生成modules.dep
2、创建设备节点文件
驱动加载成功须要在/dev目录下创建一个与之对应设备节点文件,应用程序就是通过操作设备节点文件来完成对具体设备的操作
创建/dev/chrdev设备节点文件
mknod/dev/chrdevc2000
mknod:创建节点命令
/dev/chrdev:要创建的节点文件
c:字符设备
2000:设备的主设备号和次设备号
创建完成之后才会存在/dev/chrdev文件,可以使用ls/dev/chrdev-l命令查看
3、设备测试
运行测试程序
./xxx(程序)参数1参数2...(参数传给应用层的main函数)
./chrdevbaseApp/dev/chrdevbase1//诸如测试对chrdevbase设备的写操作4、卸载驱动模块rmmodxxx.ko
卸载后可以使用lsmod查看卸载模块是否成功
本文原创地址://q13zd.cn/zfsbqdccqsbh.html编辑:刘遄,审核员:暂无