配色: 字号:
字符设备驱动——申请、创建、应用
2021-10-13 | 阅:  转:  |  分享 
  
1、申请设备号//1、注册获取设备号//2、初始化设备//3、操作设备file_operations–openrelease
readwriteioctl…//4、两个宏定义module_initmodule_exit//5、注册设备号re
gister_chrdev_region//6、cdev_init初始化字符设备//7、cdev_add添加字符设备到系统
1)向系统申请主设备号intregister_chrdev(unsignedintmajor,constchar
name,conststructfile_operationsfops)//参数://1、major:主设备号//
设备号(32bit–dev_t)==主设备号(高12bit)+次设备号(低20bit)//主设备号:表示一类
设备—(如:camera)//次设备号:表示一类设备中某一个—(如:前置camera/后置camera)//
0-->动态分配;250-->给定整数,静态指定//2、name:描述设备信息,可自定义//在
目录/proc/devices列举出了所有的已经注册的设备//3、fops:文件操作对象//提供open,r
ead,write//返回值:成功-0,失败-负数?2)释放设备号voidunregister_chrdev(unsigned
intmajor,constcharname)3)例:主设备号的申请?chr_drv.c加载驱动前:???加载驱
动后:??2、创建设备节点1)手动创建··缺点/dev/目录中文件都是在内存中,断电后/dev/文件就会消失mknod/
dev/设备名类型主设备号次设备号(主设备号要和驱动中申请的主设备号保持一致)比如:mknod/dev/chr
0c2500eg:[root@farsightdrv_module]#ls/dev/chr0-lcrw-r
--r--100250,0Jan100:33/dev/chr02)自动创建通过udev/mdev机制struc
tclassclass_create(owner,name)//创建一个类//参数://1、owner:THIS_MODU
LE//2、name:字符串名字,自定义//返回://返回一个class指针创建一个设备文件://创建一个设备
文件structdevicedevice_create(structclassclass,structdevic
eparent,dev_tdevt,voiddrvdata,constcharfmt,...)//参数
://1、class结构体,class_create调用之后的返回值//2、表示父亲,一般直接填NULL//3、设备号类型dev
_t//4、私有数据,一般直接填NULL//5/6、表示可变参数,字符串,表示设备节点名字设备号类型:dev_tdevt#de
fineMAJOR(dev)((unsignedint)((dev)>>MINORBITS))//获取主设备号
#defineMINOR(dev)((unsignedint)((dev)&MINORMASK))//获取次设备
号#defineMKDEV(ma,mi)(((ma)<件:voiddevice_destroy(devcls,MKDEV(dev_major,0));//参数://1、clas
s结构体,class_create调用之后到返回值//2、设备号类型dev_tvoidclass_destroy(devcls
);//参数:class结构体,class_create调用之后到返回值3)示例:?chr_drv.c?3、实现文件IO接口--
fops1)驱动中实现文件io操作接口:structfile_operations1structfile_operati
ons{2structmoduleowner;3loff_t(llseek)(str
uctfile,loff_t,int);4ssize_t(read)(structfile
,char__user,size_t,loff_t);5ssize_t(write)(
structfile,constchar__user,size_t,loff_t);6ssize_t
(aio_read)(structkiocb,conststructiovec,unsign
edlong,loff_t);7ssize_t(aio_write)(structkiocb,
conststructiovec,unsignedlong,loff_t);8int(iterate)(
structfile,structdir_context);9unsignedint(poll)(str
uctfile,structpoll_table_struct);10long(unlocked_ioctl)
(structfile,unsignedint,unsignedlong);11long(compat_io
ctl)(structfile,unsignedint,unsignedlong);12int(mmap)
(structfile,structvm_area_struct);13int(open)(structi
node,structfile);14....16long(fallocate)(structfilef
ile,intmode,loff_toffset,loff_tlen);17int(show_fdinfo)(s
tructseq_filem,structfilef);18};//函数指针的集合,其实就是接口,我们写驱动到时
候需要去实现1920conststructfile_operationsmy_fops={21.open=ch
r_drv_open,22.read=chr_drv_read,23.write=chr_drv_write,24.
release=chr_drv_close,25};示例:?chr_drv1.c实现了底层的fops成员函数,再实现应用程序
的调用2)应用程序调用文件IO控制驱动:open、read...?chr_drv1.c?chr_test.c?Makefil
e测试结果;??4、应用程序控制驱动应用程序要控制驱动,就涉及用户空间与内核空间的数据交互,如何实现?通过以下函数:1)cop
y_to_user1//将数据从内核空间拷贝到用户空间,一般是在驱动中chr_drv_read()用2intcopy_to_
user(void__userto,constvoidfrom,unsignedlongn)3//参数:
4//1:应用驱动中的一个buffer5//2:内核空间到一个buffer6//3:个数7//返回值:大于0,表示出错,剩
下多少个没有拷贝成功等于0,表示正确2)copy_from_user1//将数据从用户空间拷贝到内核空间,一般是在驱动中chr
_drv_write()用2intcopy_from_user(voidto,constvoid__user
from,unsignedlongn)3//参数:4//1:内核驱动中的一个buffer5//2:应用空间到一个buf
fer6//3:个数示例:?chr_drv1.c?chr_test.c测试:??5、驱动程序控制外设之前我们了解了应用程序如何
与内核空间进行数据交互,那么内核驱动与外设间的控制是怎么样的?写过裸机程序的都知道,可以通过修改外设对应的控制寄存器来控制外设,
即向寄存器的地址写入数据,这个地址就是物理地址,且物理地址是已知的,有硬件设计决定。在内核中,同样也是操作地址控制外设,但是内核
中的地址,是经过MMU映射后的虚拟地址,而且CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,驱动程序并
不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内,然后才能根据映射所得到的核心虚地址范围,通过访内指令访
问这些I/O内存资源。Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间
中ioremap的函数如下:1//映射虚拟地址2voidioremap(cookie,size)3//参数:4//
1、cookie:物理地址5//2、size:长度(连续映射一定长度的地址空间)6//返回值:虚拟地址解除映射:1//去映射--解除映射2voidiounmap(void__iomemaddr)3//参数:映射后的虚拟地址实例:通过驱动控制LED灯LED——GPX2_7——GPX2CON——0x11000C40?GPX2DAT——??0x11000C44?将0x11000c40映射为虚拟地址?chr_drv1.c?chr_test.c测试:??执行app后,可以看到LED等以一秒的间隔亮灭
献花(0)
+1
(本文系安信实验室首藏)