简述字符设备驱动程序开发主要流程?

默认分类 未结 1 437
0o冰凌泡沫o0
0o冰凌泡沫o0 2023-03-17 14:34
相关标签:
1条回答
  • 2023-03-17 15:09

    字符设备驱动程序框架1、写出open、write函数2、告诉内核1)、定义一个structfile_operations结构并填充好staticstructfile_operationsfirst_drv_fops={.owner=THIS_MODULE,/*这是一个宏,推向编译模块时自动创建的__this_module变量*/.open=first_drv_open,.write=first_drv_write,};2)、把structfile_operations结构体告诉内核major=register_chrdev(0,"first_drv",&first_drv_fops);//注册,告诉内核相关参数:第一个,设备号,0自动分配主设备号,否则为主设备号0-255第二个:设备名第二个:structfile_operations结构体4)、register_chrdev由谁调用(入口函数调用)staticintfirst_drv_init(void)5)、入口函数须使用内核宏来修饰module_init(first_drv_init);module_init会定义一个结构体,这个结构体里面有一个函数指针指向first_drv_init这个函数,当我们加载或安装一个驱动时,内核会自动找到这个结构体,然后调用里面的函数指针,这个函数指针指向first_drv_init这个函数,first_drv_init这个函数就是把structfile_operations结构体告诉内核6)、有入口函数就有出口函数module_exit(first_drv_exit);最后加上协议MODULE_LICENSE("GPL");3、mdev根据系统信息自动创建设备节点:每次写驱动都要手动创建设备文件过于麻烦,使用设备管理文件系统则方便很多。在2.6的内核以前一直使用的是devfs,但是它存在许多缺陷。它创建了大量的设备文件,其实这些设备更本不存在。而且设备与设备文件的映射具有不确定性,比如U盘即可能对应sda,又可能对应sdb。没有足够的主/辅设备号。2.6之后的内核引入了sysfs文件系统,它挂载在/sys上,配合udev使用,可以很好的完成devfs的功能,并弥补了那些缺点。(这里说一下,当今内核已经使用netlink了)。udev是用户空间的一个应用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精简版。首先在busybox中添加支持mdev的选项:LinuxSystemUtilities--->[*]mdev[*]Support/etc/mdev.conf[*]Supportsubdirs/symlinks[*]Supportregularexpressionssubstitutionswhenrenamingdevice[*]Supportcommandexecutionatdeviceaddition/removal然后修改/etc/init.d/rcS:echo/sbin/mdev>/proc/sys/kernel/hotplug/sbin/mdev-s执行mdev-s:以‘-s’为参数调用位于/sbin目录写的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描/sys/class和/sys/block中所有的类设备目录,如果在目录中含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev下创建设备节点文件。一般只在启动时才执行一次“mdev-s”。热插拔事件:由于启动时运行了命令:echo/sbin/mdev>/proc/sys/kernel/hotplug,那么当有热插拔事件产生时,内核就会调用位于/sbin目录的mdev。这时mdev通过环境变量中的ACTION和DEVPATH,来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否“dev”的属性文件,如果有就利用这些信息为这个设备在/dev下创建设备节点文件重新打包文件系统,这样/sys目录,/dev目录就有东西了下面是create_class的原型:#defineclass_create(owner,name)/({/staticstructlock_class_key__key;/__class_create(owner,name,&__key);/})externstructclass*__must_check__class_create(structmodule*owner,constchar*name,structlock_class_key*key);class_destroy的原型如下:externvoidclass_destroy(structclass*cls);device_create的原型如下:externstructdevice*device_create(structclass*cls,structdevice*parent,dev_tdevt,void*drvdata,constchar*fmt,...)__attribute__((format(printf,5,6)));device_destroy的原型如下:externvoiddevice_destroy(structclass*cls,dev_tdevt);具体使用如下,可参考后面的实例:staticstructclass*firstdrv_class;staticstructclass_device*firstdrv_class_dev;firstdrv_class=class_create(THIS_MODULE,"firstdrv");firstdrv_class_dev=class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");/*/dev/xyz*/class_device_unregister(firstdrv_class_dev);class_destroy(firstdrv_class);下面再来看一下应用程序如何找到这个结构体的在应用程序中我们使用open打开一个设备:如:open(/dev/xxx,O_RDWR);xxx有一个属性,如字符设备为c,后面为读写权限,还有主设备名、次设备名,我们注册时通过register_chrdev(0,"first_drv",&first_drv_fops)(有主设备号,设备名,structfile_operations结构体)将first_drv_fops结构体注册到内核数组chrdev中去的,结构体中有open,write函数,那么应用程序如何找到它的,事实上是根据打开的这个文件的属性中的设备类型及主设备号在内核数组chrdev里面找到我们注册的first_drv_fops,实例代码:#include#include#include#include#include#include#include#include#include#includestaticstructclass*firstdrv_class;staticstructclass_device*firstdrv_class_dev;volatileunsignedlong*gpfcon=NULL;volatileunsignedlong*gpfdat=NULL;staticintfirst_drv_open(structinode*inode,structfile*file){//printk("first_drv_open ");/*配置GPF4,5,6为输出*/*gpfcon&=~((0x3<<(4*2))|(0x3<<(5*2))|(0x3<<(6*2)));*gpfcon|=((0x1<<(4*2))|(0x1<<(5*2))|(0x1<<(6*2)));return0;}staticssize_tfirst_drv_write(structfile*file,constchar__user*buf,size_tcount,loff_t*ppos){intval;//printk("first_drv_write ");copy_from_user(&val,buf,count);//copy_to_user();if(val==1){//点灯*gpfdat&=~((1<<4)|(1<<5)|(1<<6));}else{//灭灯*gpfdat|=(1<<4)|(1<<5)|(1<<6);}return0;}staticstructfile_operationsfirst_drv_fops={.owner=THIS_MODULE,/*这是一个宏,推向编译模块时自动创建的__this_module变量*/.open=first_drv_open,.write=first_drv_write,};intmajor;staticintfirst_drv_init(void){major=register_chrdev(0,"first_drv",&first_drv_fops);//注册,告诉内核firstdrv_class=class_create(THIS_MODULE,"firstdrv");firstdrv_class_dev=class_device_create(firstdrv_class,NULL,MKDEV(major,0),NULL,"xyz");/*/dev/xyz*/gpfcon=(volatileunsignedlong*)ioremap(0x56000050,16);gpfdat=gpfcon+1;return0;}staticvoidfirst_drv_exit(void){unregister_chrdev(major,"first_drv");//卸载class_device_unregister(firstdrv_class_dev);class_destroy(firstdrv_class);iounmap(gpfcon);}module_init(first_drv_init);module_exit(first_drv_exit);MODULE_LICENSE("GPL");编译用Makefile文件KERN_DIR=/work/system/linux-2.6.22.6all:make-C$(KERN_DIR)M=`pwd`modulesclean:make-C$(KERN_DIR)M=`pwd`modulescleanrm-rfmodules.orderobj-m+=first_drv.o测试程序:#include#include#include#include/*firstdrvteston*firstdrvtestoff*/intmain(intargc,char**argv){intfd;intval=1;fd=open("/dev/xyz",O_RDWR);if(fd<0){printf("can'topen! ");}if(argc!=2){printf("Usage: ");printf("%s ",argv[0]);return0;}if(strcmp(argv[1],"on")==0){val=1;}else{val=0;}write(fd,&val,4);return0;}

    0 讨论(0)
提交回复