ARM的嵌入式Linux移植体验之设备驱动.pptx
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ARM 嵌入式 Linux 移植 体验 设备 驱动
- 资源描述:
-
,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,2011-8-22,#,ARM,的嵌入式,Linux,移植体验之设备驱动,前言,1.,内存分配,2.,中断,3.,块设备驱动,4.,小结,设备驱动程序是操作系统内核和机器硬件之间的接口,它为应用程序屏蔽硬件的细节,一般来说,,Linux,的设备驱动程序需要完成如下功能:,设备初始化、释放;,提供各类设备服务;,负责内核和设备之间的数据交换;,检测和处理设备工作过程中出现的错误。,前言,Linux,下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得,Windows,的设备操作犹如文件一般。在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如,open(),、,close(),、,read(),、,write(),等。,Linux,主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。在对字符设备发出读,/,写请求时,实际的硬件,I/O,一般就紧接着发生了;而块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的,I/O,操作。块设备主要针对磁盘等慢速设备。,由于,Linux,驱动程序在内核中运行,因此在设备驱动程序需要申请,/,释放内存时,不能使用用户级的,malloc/free,函数,而需由内核级的函数,kmalloc/kfree(),来实现,,kmalloc(),函数的原型为:,void kmalloc(size_t size,int priority);,参数,size,为申请分配内存的字节数,,kmalloc,最多只能开辟,128k,的内存;参数,priority,说明若,kmalloc(),不能马上分配内存时用户进程要采用的动作:,GFP_KERNEL,表示等待,即等,kmalloc(),函数将一些内存安排到交换区来满足你的内存需要,,GFP_ATOMIC,表示不等待,如不能立即分配到内存则返回,0,值;函数的返回值指向已分配内存的起始地址,出错时,返回,0,。,1.,内存分配,kmalloc(),分配的内存需用,kfree(),函数来释放,,kfree(),被定义为:,#define kfree(n)kfree_s(n),0),其中,kfree_s(),函数原型为:,void kfree_s(void*ptr,int size);,参数,ptr,为,kmalloc(),返回的已分配内存的指针,,size,是要释放内存的字节数,若为,0,时,由内核自动确定内存的大小。,许多设备涉及到中断操作,因此,在这样的设备的驱动程序中需要对硬件产生的中断请求提供中断服务程序。与注册基本入口点一样,驱动程序也要请求内核将特定的中断请求和中断服务程序联系在一起。在,Linux,中,用,request_irq(),函数来实现请求:,int request_irq(unsigned int irq,void(*handler)int,unsigned long type,char*name);,参数,irq,为要中断请求号,参数,handler,为指向中断服务程序的指针,参数,type,用来确定是正常中断还是快速中断(正常中断指中断服务子程序返回后,内核可以执行调度程序来确定将运行哪一个进程;而快速中断是指中断服务子程序返回后,立即执行被中断程序,正常中断,type,取值为,0,,快速中断,type,取值为,SA_INTERRUPT,),参数,name,是设备驱动程序的名称。,2.,中断,块设备驱动程序的编写是一个浩繁的工程,其难度远超过字符设备,上千行的代码往往只能搞定一个简单的块设备,而数十行代码就可能搞定一个字符设备。因此,非得有相当的基本功才能完成此项工作。下面先给出一个实例,即,mtdblock,块设备的驱动。我们通过分析此实例中的代码来说明块设备驱动程序的写法(由于篇幅的关系,大量的代码被省略,只保留了必要的主干):,#include#include static void mtd_notify_add(struct mtd_info*mtd);static void mtd_notify_remove(struct mtd_info*mtd);static struct mtd_notifier notifier=,mtd_notify_add,mtd_notify_remove,NULL;,3,.,块设备驱动,static devfs_handle_t devfs_dir_handle=NULL;static devfs_handle_t devfs_rw_handleMAX_MTD_DEVICES;,static struct mtdblk_dev,struct mtd_info*mtd;/*Locked*/,int count;,struct semaphore cache_sem;,unsigned char*cache_data;,unsigned long cache_offset;,unsigned int cache_size;,enum STATE_EMPTY,STATE_CLEAN,STATE_DIRTY cache_state;*mtdblksMAX_MTD_DEVICES;,static spinlock_t mtdblks_lock;/*this lock is used just in kernels=2.5.x*/static spinlock_t mtdblock_lock;,static int mtd_sizesMAX_MTD_DEVICES;static int mtd_blksizesMAX_MTD_DEVICES;,static void erase_callback(struct erase_info*done),wait_queue_head_t*wait_q=(wait_queue_head_t*)done-priv;,wake_up(wait_q);,static int erase_write(struct mtd_info*mtd,unsigned long pos,int len,const char*buf),struct erase_info erase;,DECLARE_WAITQUEUE(wait,current);,wait_queue_head_t wait_q;,size_t retlen;,int ret;,/*,*First,lets erase the flash block.,*/,init_waitqueue_head(,erase.mtd=mtd;,erase.callback=erase_callback;,erase.addr=pos;,erase.len=len;,erase.priv=(u_long),set_current_state(TASK_INTERRUPTIBLE);,add_wait_queue(,ret=MTD_ERASE(mtd,if(ret),set_current_state(TASK_RUNNING);,remove_wait_queue(,printk(KERN_WARNING mtdblock:erase of region 0 x%lx,0 x%x on/%s/failed/n,pos,len,mtd-name);,return ret;,schedule();/*Wait for erase to finish.*/,remove_wait_queue(,/*,*Next,writhe data to flash.,*/,ret=MTD_WRITE(mtd,pos,len,if(ret),return ret;,if(retlen!=len),return-EIO;,return 0;,static int write_cached_data(struct mtdblk_dev*mtdblk),struct mtd_info*mtd=mtdblk-mtd;,int ret;,if(mtdblk-cache_state!=STATE_DIRTY),return 0;,DEBUG(MTD_DEBUG_LEVEL2,mtdblock:writing cached data for/%s/at 0 x%lx,size 0 x%x/n,mtd-name,mtdblk-cache_offset,mtdblk-cache_size);,ret=erase_write(mtd,mtdblk-cache_offset,mtdblk-cache_size,mtdblk-cache_data);,if(ret),return ret;,mtdblk-cache_state=STATE_EMPTY;,return 0;,static int do_cached_write(struct mtdblk_dev*mtdblk,unsigned long pos,int len,const char*buf),static int do_cached_read(struct mtdblk_dev*mtdblk,unsigned long pos,int len,char*buf),static int mtdblock_open(struct inode*inode,struct file*file),static release_t mtdblock_release(struct inode*inode,struct file*file),int dev;,struct mtdblk_dev*mtdblk;,DEBUG(MTD_DEBUG_LEVEL1,mtdblock_release/n);,if(inode=NULL),release_return(-ENODEV);,dev=minor(inode-i_rdev);,mtdblk=mtdblksdev;,down(,write_cached_data(mtdblk);,up(,spin_lock(,if(!-mtdblk-count),/*It was the last usage.Free the device*/,mtdblksdev=NULL;,spin_unlock(,if(mtdblk-mtd-sync),mtdblk-mtd-sync(mtdblk-mtd);,put_mtd_device(mtdblk-mtd);,vfree(mtdblk-cache_data);,kfree(mtdblk);,else,spin_unlock(,DEBUG(MTD_DEBUG_LEVEL1,ok/n);,BLK_DEC_USE_COUNT;,release_return(0);,/*This is a special request_fn because it is executed in a process context*to be able to sleep independently of the caller.The*io_request_lock(for=2.5)is held upon entry*and exit.The head of our request queue is considered active so there is*no need to dequeue requests before we are done.*/static void handle_mtdblock_request(void),struct request*req;,struct mtdblk_dev*mtdblk;,unsigned int res;,for(;),INIT_REQUEST;,req=CURRENT;,spin_unlock_irq(QUEUE_LOCK(QUEUE);,mtdblk=mtdblksminor(req-rq_dev);,res=0;,if(minor(req-rq_dev)=MAX_MTD_DEVICES),panic(%s:minor out of bound,_FUNCTION_);,if(!IS_REQ_CMD(req),goto end_req;,if(req-sector+req-current_nr_sectors)(mtdblk-mtd-size 9),goto end_req;,/Handle the request,switch(rq_data_dir(req),int err;,case READ:,down(,err=do_cached_read(mtdblk,req-sector current_nr_sectors buffer);,up(,if(!err),res=1;,break;,case WRITE:,/Read only device,if(!(mtdblk-mtd-flags&MTD_WRITEABLE),break;,/Do the write,down(,err=do_cached_write(mtdblk,req-sector current_nr_sectors buffer);,up(,if(!err),res=1;,break;,end_req:,spin_lock_irq(QUEUE_LOCK(QUEUE);,end_request(res);,static volatile int leaving=0;static DECLARE_MUTEX_LOCKED(thread_sem);static DECLARE_WAIT_QUEUE_HEAD(thr_wq);,int mtdblock_thread(void*dummy),#define RQFUNC_ARG request_queue_t*q,static void mtdblock_request(RQFUNC_ARG),/*Dont do anything,except wake the thread if necessary*/,wake_up(,static int mtdblock_ioctl(struct inode*inode,struct file*file,unsigned int cmd,unsigned long arg),struct mtdblk_dev*mtdblk;,mtdblk=mtdblksminor(inode-i_rdev);,switch(cmd),case BLKGETSIZE:/*Return device size*/,return put_user(mtdblk-mtd-size 9),(unsigned long*)arg);,case BLKFLSBUF:,if(!capable(CAP_SYS_ADMIN),return-EACCES;,fsync_dev(inode-i_rdev);,invalidate_buffers(inode-i_rdev);,down(,write_cached_data(mtdblk);,up(,if(mtdblk-mtd-sync),mtdblk-mtd-sync(mtdblk-mtd);,return 0;,default:,return-EINVAL;,static struct block_device_operations mtd_fops=,owner:THIS_MODULE,open:mtdblock_open,release:mtdblock_release,ioctl:mtdblock_ioctl;,static void mtd_notify_add(struct mtd_info*mtd),static void mtd_notify_remove(struct mtd_info*mtd),if(!mtd|mtd-type=MTD_ABSENT),return;,devfs_unregister(devfs_rw_handlemtd-index);,int _init init_mtdblock(void),int i;,spin_lock_init(,/*this lock is used just in kernels=2.5.x*/,spin_lock_init(,#ifdef CONFIG_DEVFS_FS,if(devfs_register_blkdev(MTD_BLOCK_MAJOR,DEVICE_NAME,&mtd_fops),printk(KERN_NOTICE Cant allocate major number%d for Memory Technology Devices./n,MTD_BLOCK_MAJOR);,return-EAGAIN;,devfs_dir_handle=devfs_mk_dir(NULL,DEVICE_NAME,NULL);,register_mtd_user(,#else,if(register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops),printk(KERN_NOTICE Cant allocate major number%d for Memory Technology Devices./n,MTD_BLOCK_MAJOR);,return-EAGAIN;,#endif,/*We fill it in at open()time.*/for(i=0;i MAX_MTD_DEVICES;i+),mtd_sizesi=0;,mtd_blksizesi=BLOCK_SIZE;init_waitqueue_head(,BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR),kernel_thread(mtdblock_thread,NULL,CLONE_FS|CLONE_FILES|CLONE_SIGHAND);return 0;,static void _exit cleanup_mtdblock(void),leaving=1;,wake_up(,down(,#ifdef CONFIG_DEVFS_FS,unregister_mtd_user(,devfs_unregister(devfs_dir_handle);,devfs_unregister_blkdev(MTD_BLOCK_MAJOR,DEVICE_NAME);,#else,unregister_blkdev(MAJOR_NR,DEVICE_NAME);,#endif,blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR);,blksize_sizeMAJOR_NR=NULL;,blk_sizeMAJOR_NR=NULL;,module_init(init_mtdblock);module_exit(cleanup_mtdblock);,从上述源代码中我们发现,块设备也以与字符设备,register_chrdev,、,unregister_ chrdev,函数类似的方法进行设备的注册与释放:,int register_blkdev(unsigned int major,const char*name,struct block_device_operations*bdops);int unregister_blkdev(unsigned int major,const char*name);,但是,,register_chrdev,使用一个向,file_operations,结构的指针,而,register_blkdev,则使用,block_device_operations,结构的指针,其中定义的,open,、,release,和,ioctl,方法和字符设备的对应方法相同,但未定义,read,或者,write,操作。这是因为,所有涉及到块设备的,I/O,通常由系统进行缓冲处理。,块驱动程序最终必须提供完成实际块,I/O,操作的机制,在,Linux,当中,用于这些,I/O,操作的方法称为,request,(请求),。在块设备的注册过程中,需要初始化,request,队列,这一动作通过,blk_init_queue,来完成,,blk_init_queue,函数建立队列,并将该驱动程序的,request,函数关联到队列。在模块的清除阶段,应调用,blk_cleanup_queue,函数。,本例中相关的代码为:,BLK_INIT_QUEUE(BLK_DEFAULT_QUEUE(MAJOR_NR),每个设备有一个默认使用的请求队列,必要时,可使用,BLK_DEFAULT_QUEUE(major),宏得到该默认队列。这个宏在,blk_dev_struct,结构形成的全局数组(该数组名为,blk_dev,)中搜索得到对应的默认队列。,blk_dev,数组由内核维护,并可通过主设备号索引。,blk_dev_struct,接口定义如下:,struct blk_dev_struct,/*,*queue_proc has to be atomic,*/,request_queue_t request_queue;,queue_proc*queue;,void*data;,request_queue,成员包含了初始化之后的,I/O,请求队列,,data,成员可由驱动程序使用,以便保存一些私有数据。,request_queue,定义为:,struct request_queue,/*,*the queue request freelist,one for reads and one for writes,*/,struct request_list rq2;,/*,*Together with queue_head for cacheline sharing,*/,struct list_head queue_head;,elevator_t elevator;,request_fn_proc*request_fn;,merge_request_fn*back_merge_fn;,merge_request_fn*front_merge_fn;,merge_requests_fn*merge_requests_fn;,make_request_fn*make_request_fn;,plug_device_fn*plug_device_fn;,/*,*The queue owner gets to use this for whatever they like.,*ll_rw_blk doesnt touch it.,*/,void*queuedata;,/*,*This is used to remove the plug when tq_disk runs.,*/,struct tq_struct plug_tq;,/*,*Boolean that indicates whether this queue is plugged or not.,*/,char plugged;,/*,*Boolean that indicates whether current_request is active or,*not.,*/,char head_active;,/*,*Is meant to protect the queue in the future instead of,*io_request_lock,*/,spinlock_t queue_lock;,/*,*Tasks wait here for free request,*/,wait_queue_head_t wait_for_request;,本章讲述了,Linux,设备驱动程序的入口函数及驱动程序中的内存申请、中断等,并分别以实例讲述了字符设备及块设备的驱动开发方法。,4.,小结,展开阅读全文
咨信网温馨提示:1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。




ARM的嵌入式Linux移植体验之设备驱动.pptx



实名认证













自信AI助手
















微信客服
客服QQ
发送邮件
意见反馈



链接地址:https://www.zixin.com.cn/doc/13188745.html