首页 linux驱动程序设计实例

linux驱动程序设计实例

举报
开通vip

linux驱动程序设计实例AT91SAM9G20驱动程序设计开发环境:Vmware+ubuntu10.04硬件平台:AT91SAM9G20Linux版本:一:led驱动说明:因为设计的开发板上没有led灯,便通过PCO来演示,通过示波器来观察引脚端的电平变化。1.驱动程序:my_led.c#include#include#include#include#include#include#include#defineMY_LED_MAJOR250//定义主设备号#defineLED_ON0#defineLED_OFF1structglobal_...

linux驱动程序设计实例
AT91SAM9G20驱动程序设计开发环境:Vmware+ubuntu10.04硬件平台:AT91SAM9G20Linux版本:一:led驱动说明:因为设计的开发板上没有led灯,便通过PCO来演示,通过示波器来观察引脚端的电平变化。1.驱动程序:my_led.c#include#include#include#include#include#include#include#defineMY_LED_MAJOR250//定义主设备号#defineLED_ON0#defineLED_OFF1structglobal_dev{structcdevcdev;};〃定义设备结构体structglobal_dev*global_devp;〃定义一个指向设备结构体的指针staticintmy_led_open(structinode*inode,structfile*filp){--filp->private_data=global_devp;return0;}staticintmy_led_release(structinode*inode,structfile*file){return0;}staticintmy_led_ioctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongdata){switch(cmd){caseLED_ON:at91_set_gpio_value(AT91_PIN_PC0,0);//将PC0引脚置低break;caseLED_OFF:at91_set_gpio_value(AT91_PIN_PC0,1);//将PC1引脚置高break;default:printk("novalidcmdinput!\n");break;}return0;}structfile_operationsmy_led_ctl_ops={.owner=THIS_MODULE,.open=my_led_open,.release=my_led_release,.ioctl=my_led_ioctl,};/*初始化设备结构体*/staticvoidmy_led_setup(structglobal_dev*dev,intindex){---interr;intdevno=MKDEV(MY_LED_MAJOR,index);cdev_init(&dev->cdev,&my_led_ctl_ops);dev->cdev.owner=THIS_MODULE;dev->cdev.ops=&my_led_ctl_ops;err=cdev_add(&dev->cdev,devno,1);if(err)printk("addmyledsetupfailed!\n");}staticintmy_led_init(void){--intret;dev_tdevno=MKDEV(MY_LED_MAJOR,0);〃创建设备号printk("myfirstdriver--led!\n");at91_set_GPIO_periph(AT91_PIN_PC0,1);at91_set_gpio_output(AT91_PIN_PC0,1);〃对PC0引脚的初始化ret=register_chrdev_region(devno,1,"my_led");//申请设备号if(ret<0){printk("my_ledinit_modulefailedwith%d\n",ret);returnret;}elseprintk("my_ledinit_modulesuccess!\n");global_devp=kmalloc(sizeof(structglobal_dev),GFP_KERNEL);//申请设备内存memset(global_devp,0,sizeof(structglobal_dev));my_led_setup(global_devp,0);returnret;}staticvoidmy_led_cleanup(void){--cdev_del(&global_devp->cdev);//删除设备kfree(global_devp);//释放内存unregister_chrdev_region(MKDEV(MY_LED_MAJOR,0),1);//释放设备号}MODULE_LICENSE("MYGPL");MODULE_AUTHOR("FANY");module_init(my_led_init);//注册设备module_exit(my_led_cleanup);〃卸载设备2:如何将驱动驱动程序编译成模块在drivers目录下新建led目录,并在该目录下添加Kconfig,Makefile文件。Kconfig:Menu"Mydriversupport"ConfigTrisate"leddriver!"HelpLeddriverEndmenu:Makefile:Obj-$(CONFIG_MY_LED)+=my_led.o修改linux/drivers目录下的Kconfig,Makefile文件Kconfig:Source"drivers/led/Kconfig"Makefile:Obj-y+=my_led/修改体系结构目录arch/arm目录下的Kconfig文件,否则在配置菜单中将无法看到led的配置选项。(如果是在drivers目录下新建一文件夹,并在其中添加驱动程序,必须相应的体系结构目录下添加配置选项)。Kconfig:Source"driver/led/Kconfig"3.测试程序:my_led_test.c#include#include#include#include#include#defineDEVICE_NAME"/dev/my_led"#defineLED_ON0#defineLED_OFF1intmain(void){intfd;intret;inti;printf("my_led_drivertest!\n");fd=open(DEVICE_NAME,O_RDONLY);if(fd==-1)printf("opendevice%serror!\n",DEVICE_NAME);for(i=0;i<50;i++){ioctl(fd,LED_OFF);sleep(1);ioctl(fd,LED_ON);sleep(1);}ret=close(fd);printf("ret=%d\n",ret);printf("closemy_led_driver!\n");return0;}将测试程序编译成目标平台的可执行文件,并下载到开发板GCC=/home/zzq/9G20/arm-2007q1/bin/arm-none-linux-gnueabi-gcc#交叉编译器的路径My_led_test:my_led_test.c$(GCC)-omy_led_testmy_led_test.cclean:rm-fmy_led_test学习总结:熟悉驱动程序的架构,如何将驱动程序添加到内核即如何写测试程序。二:按键驱动设计1•硬件部分:PC4接按键。2•驱动程序:#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#defineBUTTON_MAJOR245#defineDEVICE_NAME"/dev/button"staticvolatileintev_press=0;staticstructcdevbutton_cdev;staticvoidbutton_do_tasklet(unsignedlongn);DECLARE_TASKLET(button_tasklet,button_do_tasklet,0);//定义tasklet并与处理函数关联起来staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);〃静态的初始化一个等待队列structbutton_irq_desc{intirq;intirq_type;intpin;intnumber;char*name;};staticstructbutton_irq_descbutton_irq[1]={{AT91_PIN_PB22,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB22,0,"KEY0"}};staticintkey_values[1]={0};〃中断处理底半部staticvoidbutton_do_tasklet(unsignedlongn){wake_up_interruptible(&button_waitq);//唤醒队列printk("buttonpress!\n");}〃中断处理顶半部staticirqreturn_tbutton_interrupt(intirq,void*dev_id,structpt_regs*regs){intup;staticintpress_down;up=gpio_get_value(button_irq[0].pin);printk("irq\n");/*按键消抖*/if(up)press_down=1;〃当按键没有按下,置标志位为1.if(!up&&(press_down==1)){press_down=0;//当按键按下,置标志位为0.ev_press=1;at91_set_gpio_value(button_irq[0].pin,1);key_values[button_irq[0].number]=!up;tasklet_schedule(&button_tasklet);}returnIRQ_RETVAL(IRQ_HANDLED);}staticintbutton_open(structinode*inode,structfile*filp){return0;}staticintbutton_release(structinode*inode,structfile*filp){return0;}staticintbutton_read(structfile*filp,char__user*buff,size_tcount,loff_t*offp){intret;if(!ev_press){〃当按键没有按下时,读进程挂起,知道按键按下。wait_event_interruptible(button_waitq,ev_press);}ev_press=0;ret=copy_to_user(buff,(constvoid*)key_values,min(sizeof(key_values),count));memset((void__user*)key_values,0,sizeof(key_values));returnret?-EFAULT:min(sizeof(key_values),count);}staticstructfile_operationsbutton_fops={.owner=THIS_MODULE,.open=button_open,.release=button_release,.read=button_read,};staticintirq_init(void){interr;at91_set_gpio_input(button_irq[0].pin,1);at91_set_deglitch(button」rq[0].pin,1);//将PCO设置为中断功能set_irq_type(button_irq[0].irq,button」rq[O].irq_type);〃设置中断类型at91_set_gpio_value(button_irq[0].pin,1);err=request_irq(button_irq[0].irq,button_interrupt,IRQF_DISABLED,\button」rq[0].name,(void*)&button_irq[0]);//申请中断if(err){disable_irq(button」rq[0].irq);free_irq(button」rq[0].irq,(void*)&button_irq[0]);return-EBUSY;}return0;}staticint__initbutton_init(void){intret,err;ret=register_chrdev_region(MKDEV(BUTTON_MAJOR,0),1,DEVICE_NAME);if(ret<0){printk("buttoninitfailedwith%d\n",ret);returnret;}cdev_init(&button_cdev,&button_fops);button_cdev.owner=THIS_MODULE;button_cdev.ops=&button_fops;err=cdev_add(&button_cdev,MKDEV(BUTTON_MAJOR,0),1);if(err<0){printk("keyaddfailed'n");returnerr;}irq」nit();printk("keydriveraddsuccess!\n");return0;}staticvoid__exitbutton_exit(void){cdev_del(&button_cdev);unregister_chrdev_region(MKDEV(BUTTON_MAJOR,0),1);disable_irq(button」rq[0].irq);free_irq(button」rq[0].irq,(void*)&button_irq[0]);printk("unregisterkeydriver!\n");}module_init(button_init);module_exit(button_exit);MODULE_AUTHOR("fany");MODULE_DESCRIPTION("Atmel9g20keyDriver");MODULE_LICENSE("GPL");3•测试程序:#include#include#include#include#include#include#defineDEVICE_NAME"/dev/button"intmain(void){intfd,i;intret;intkey_value[1];printf("keytest!\n");fd=open(DEVICE_NAME,O_RDWR);if(fd<0)printf("opendevice%serror!\n",DEVICE_NAME);elseprintf("opendevicesuccess!\n");while(1){ret=read(fd,key_value,1);if(!ret){printf("buttonnotpress!\n");}elseprintf("buttonpress!\n");printf("key_value%d\n",key_value);}close(fd);printf("closekeydriver!\n");return0;}4•学习总结:在linux设备驱动程序中,中断处理程序通常分为两部分:上半部和下半部。上半部处理比较紧急的的硬件操作,比如简单的读取寄存器中的状态并清除中断标志后,进行登记中断的工作。剩下的工作就由下半部来实现。对阻塞与非阻塞进程的理解,阻塞:在执行设备操作时,若不能获取设备资源则挂起,直到满足可操作的条件后再进行操作。非阻塞操作:在执行设备操作时,若不能获取设备资源则立即返回。三:总线驱动AT91SAM9G20存储器映射图(截取部分),片选4接外设,利用总线对外设进行访问。Figure8-1.AT91SAM9G20MemoryMappingssoacoco&(CF*F*FFFInternal256MByffiSGjcTO0Q00OxTOaMOEloolntBray门》ROMCxlGOCCOGOEB!\t逊DOMOtIFFFFFFFChipS&^Ct325SMB/lee\GX204D30SRALWEBlChpI\FMur-vMiQaOO0X025BMSRAMiSDR.WC\Dx3Q4K0t:x2FFFFF^FOx3COCC3C0\QsMtWOEBlChipS&^ct2256M3yl$B\二nUHP:kJFFFFF^FCx4ClCMODEB!\C-pSfilsstS256帖SylSG-:X:FFFFF-F气ANDFlmsrCHiF-F^FFF0x50000000EBl匸■F4CompactFlasn256M3yle'EOmsF-p-fffAddresshtemorySp白鈕InlennalfJ白mtiF*胡白pping32Kny:eatSK目加■flKByUd但是在linux驱动,不能对物理地址进行操作,可通过内存访问的机制实现对物理地址的访问。将一段物理地址空间映射到虚拟地址空间中,然后对虚拟地址的操作即是对物理地址的操作4WriteWrite物理地址requestmemregion——t虚拟地址■■ReadRead■对虚拟地址的读写即是对物理地址的读写3.1:总线驱动#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include"atmel9g20_liu.h"structgr_liu_info{void__iomem*virtbase;void__iomem*regbase;structresource*res;u32flags;};staticstructgr_liu_infoliu_info;staticstructcdevatmel9g20_liu_cdev;unsignedshortliu_read(unsignedaddr){addr&=ATMEL9G20_LIU_MASK;addr=addr<<1;addr+=(unsignedlong)liu_info.regbase;printk("readthevirtualaddris0x%x\n",addr);returnreadw(addr)&0xff;//读IO内存。}EXPORT_SYMBOL(liu_read);intliu_write(unsignedaddr,unsignedval){addr&=ATMEL9G20_LIU_MASK;addr=addr<<1;addr+=(unsignedlong)liu_info.regbase;printk("writethevirtualaddris0x%x\n",addr);writew(val&0xff,addr);//写IO内存return0;}EXPORT_SYMBOL(liu_write);staticintatmel9g20_liu_open(structinode*inode,structfile*filp){return0;}staticintatmel9g20_liu_release(structinode*inode,structfile*filp){return0;}staticintatmel9g20_liu_ioctl(structinode*inode,structfile*filp,unsignedcmd,unsignedlongarg){intret;liu_ctlf_tctlf;switch(cmd){caseLIU_IOCSET:ret=copy_from_user(&ctlf,(liu_ctlf_t__user*)arg,sizeof(liu_ctlf_t));if(ret)returnret;printk("inputaddrisOx%x,inputvalisOx%x\n",ctlf.addr,ctlf.val);liu_write(ctlf.addr,ctlf.val);returnret;caseLIU」0CGET:ret=copy_from_user(&ctlf,(liu_ctlf_t__user*)arg,sizeof(liu_ctlf_t));if(ret)returnret;ctlf.val=liu_read(ctlf.addr);if(copy_to_user((void__user*)arg,&ctlf,sizeof(liu_ctlf_t)))return-EFAULT;returnret;}return-ENOTTY;}staticconststructfile_operationsatmel9g20_liu_fops={.owner=THIS_MODULE,.open=atmel9g20_liu_open,.ioctl=atmel9g20_liu_ioctl,.release=atmel9g20_liu_release,};staticint__devinitliu_probe(structplatform_device*pdev){intret=0;if(pdev->num_resources!=1){ret=-ENODEV;gotoerr1;}//IO内存的申请liu」nfo.res=request_mem_region(pdev->resource[0].start,pdev->resource[0].endpdev->resource[0].start+1,"9g20-liu");if(!liu」nfo.res){printk("liu:can'tgetrequestmemregion");ret=-ENOMEM;gotoerr1;}//IO内存的映射liu_info.regbase=ioremap(pdev->resource[0].start,pdev->resource[0].end-pdev->resource[0].start+1);printk("requestvirtualaddrliu_info.regbase=0x%x\n",(unsignedint)liu_info.regbase);if(liu_info.regbase==NULL){printk("liu:can'tgetioremapmem\n");ret=-ENOMEM;gotoerr2;}dev_set_drvdata(&pdev->dev,&liu_info);printk("LIUprobedone!\n");return0;err2:release_mem_region(pdev->resource[0].start,pdev->resource[0].end-pdev->resource[0].start+1);errl:returnret;}staticintliu_remove(structplatform_device*pdev){structgr_liu」nfo*fi;fi=pdev->dev.driver_data;iounmap(fi->regbase);〃解除映射,release_mem_region(pdev->resource[0].start,pdev->resource[0].end-pdev->resource[0].start+1);//释放内存fi->flags=0x0;return0;}staticstructplatform_driverliu_driver={.probe=liu_probe,.remove=liu_remove,.driver={.name="9g20-liu",.owner=THIS_MODULE,},};〃设置SMC寄存器staticvoidcs4_init(void){at91_sys_write(AT91_MATRIX_EBICSA,at91_sys_read(AT91_MATRIX_EBICSA)|AT91_MATRIX_CS4A_SMC);/*ConfigureSMCCS4*/at91_sys_write(AT91_SMC_SETUP(4),AT91_SMC_NWESETUP_(2)|AT91_SMC_NCS_WRSETUP_(2)|AT91_SMC_NRDSETUP_(2)|AT91_SMC_NCS_RDSETUP_(2));at91_sys_write(AT91_SMC_PULSE(4),AT91_SMC_NWEPULSE_(12)|AT91_SMC_NCS_WRPULSE_(10)|AT91_SMC_NRDPULSE_(12)|AT91_SMC_NCS_RDPULSE_(10));at91_sys_write(AT91_SMC_CYCLE(4),AT91_SMC_NWECYCLE_(16)|AT91_SMC_NRDCYCLE_(16));at91_sys_write(AT91_SMC_MODE(4),AT91_SMC_READMODE|AT91_SMC_WRITEMODE|AT91_SMC_DBW_16|AT91_SMC_EXNWMODE_DISABLE|AT91_SMC_TDF_(3));at91_sys_write(AT91_PMC_PCER,1<#include#include#include#include#include#include"/dev/liu"」OW('i',30,liu_ctlf_t)」OWR('i',31,liu_ctlf_t)#defineDRIVER_LIU_FILE#definel_IU」OCSET#definel_IU」OCGETtypedefstructliu_ctlf{unsignedaddr;unsignedval;}liu_ctlf_t;//写IO内存unsignedintLiuWrite(unsignedaddr,unsigneddata){intliu_fd;liu_ctlf_tctlf;liu_fd=open(DRIVER_LIU_FILE,O_RDWR,0666);if(liu_fd<0){printf("openliufileerror!\n");return-1;}ctlf.addr=addr;ctlf.val=data&0xffff;ioctl(liu_fd,LIU」OCSET,&ctlf);close(liu_fd);return(0);}〃读IO内存unsignedintLiuRead(unsignedaddr){intliu_fd;liu_ctlf_tctlf;liu_fd=open(DRIVER_LIU_FILE,O_RDONLY,0666);if(liu_fd<0){printf("openliufileerror!\n");return-1;}ctlf.addr=addr;ctlf.val=0;ioctl(liu_fd丄IU」OCGET,&ctlf);close(liu_fd);return((ctlf.val)&0xffff);intmain(intargc,constchar**argv){unsignedintLiuAddr,value,readBack;if(argc<3){printf("writeFpgaRegaddrvalue\n");return-1;}sscanf(argv[1],"%x",&LiuAddr);sscanf(argv[2],"%x",&value);LiuWrite(LiuAddr,value);readBack=LiuRead(LiuAddr);printf("readback:0x%0x=0x%x\n",LiuAddr,readBack);return0;}输入命令的格式为:./testaddrval片选4的物理地址为0x,申请一段从0x开始的一段内存空间,其大小为0xffff,并将这段地址空间做映射,在上层对物理地址的操作既是对这一段虚拟地址空间的操作。4:当多个执行单元,同时并行执行,在获取共享资源时,可能发生冲突,如何利用信号量来保护临界区代码,解决冲突。#include#include#include#include#include#include#include#include#include#include#include#include#include#defineMEMDEV_MAJOR200#defineMEM_SIZE100structmem_dev{structcdevcdev;unsignedcharmem[MEM_SIZE];structsemaphoresem;//定义信号量};structmem_dev*mem_devp;intmemdev_open(structinode*inode,structfile*filp){filp->private_data=mem_devp;return0;}intmemdev_release(structinode*inode,structfile*filp){return0;}staticssize_tmemdev_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos){unsignedlongp=*ppos;unsignedintcount=size;intret=0;structmem_dev*dev=filp->private_data;if(p>MEM_SIZE)returncount;if(count>MEM_SIZE-p)count=MEM_SIZE-p;if(down_interruptible(&dev->sem)){〃获取信号量return-ERESTARTSYS;}if(copy_to_user(buf,(void*)(dev->mem+p),count)){return-EFAULT;}else{*ppos+=count;ret=count;printk("read%dbytesfrom%d\n",count,(unsignedint)p);}up(&dev->sem);//释放信号量returnret;}staticssize_tmemdev_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos){unsignedlongp=*ppos;unsignedcount=size;intret=0;structmem_dev*dev=filp->private_data;if(p>MEM_SIZE)returncount;if(count>MEM_SIZE-p)count=MEM_SIZE-p;if(copy_from_user(dev->mem+p,buf,count))return-EFAULT;else{*ppos+=count;ret=count;printk("write%dbytesfrom%d\n",count,(unsignedint)p);}returnret;}staticloff_tmemdev_llseek(structfile*filp,loff_toffset,intorig){switch(orig){caseSEEK_SET:if((offset<0)||(offset>MEM_SIZE))return-EINVAL;filp->f_pos=(unsignedint)offset;break;caseSEEK_CUR:if(((filp->f_pos+offset)>MEM_SIZE)||((filp->f_pos+offset)<0))return-EINVAL;filp->f_pos=filp->f_pos+offset;break;default:return-EINVAL;break;}returnfilp->f_pos;}staticconststructfile_operationsmem_fops={.owner=THIS_MODULE,.open=memdev_open,.release=memdev_release,.read=memdev_read,.write=memdev_write,.llseek=memdev_llseek,};staticvoidmemdev_setup_cdev(structmem_dev*dev,intindex){interr;dev_tdevno=MKDEV(MEMDEV_MAJOR,index);cdev_init(&dev->cdev,&mem_fops);dev->cdev.owner=THIS_MODULE;dev->cdev.ops=&mem_fops;err=cdev_add(&dev->cdev,devno,1);if(err<0){printk("addmemdevfailed!\n");}}staticintmemdev_init(void){intret;dev_tdevno=MKDEV(MEMDEV_MAJOR,0);ret=register_chrdev_region(devno,1,"/dev/memdev");if(ret<0){printk("registermemdevfailed\n");returnret;}mem_devp=kmalloc(sizeof(structmem_dev),GFP_KERNEL);if(!mem_devp){printk("kmallocfailed!\n");}memset(mem_devp,0,sizeof(structmem_dev));memdev_setup_cdev(mem_devp,0);init_MUTEX(&mem_devp->sem);〃初始化信号量printk("memdevinit!\n");return0;}staticvoidmemdev_exit(void){cdev_del(&mem_devp->cdev);kfree(mem_devp);unregister_chrdev_region(MKDEV(MEMDEV_MAJOR,0),1);printk("memdevexit!\n");}MODULE_LICENSE("GPL");MODULE_DESCRIPTION("MEMDRIVERS");MODULE_AUTHOR("FANY");module_init(memdev_init);module_exit(memdev_exit);仅供个人用于学习、研究;不得用于商业用途Forpersonaluseonlyinstudyandresearch;notforcommercialuse.Nurfurdenpers?nlichenfurStudien,Forschung,zukommerziellenZweckenverwendetwerden.Pourl'etudeetlarechercheuniquementadesfinspersonnelles;pasadesfinscommerciales.to员bkog^A.nrogeHKOTOpMeno^b3ymoiflCH6yHeHuac^egoBuHHuefigo^^HMucno员B30BaTbCEbKOMMepqeckuxqe员ex.以下无正文
本文档为【linux驱动程序设计实例】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_808969
暂无简介~
格式:doc
大小:32KB
软件:Word
页数:17
分类:
上传时间:2018-05-18
浏览量:1