Linux设备驱动程序——简单字符设备驱动程序Y-Kee转载请注明来自于衡阳师范学院08电2Y-Kee:843308498一、重要知识点1.主次设备号dev_tdev_t是内核中用来表示设备编号的数据类型;intMAJOR(dev_tdev)intMINOR(dev_tdev)这两个宏抽取主次设备号。dev_tMKDEV(unsignedintmajor,unsignedintminor)这个宏由主/次设备号构造一个dev_t结构。2.分配和释放设备号intregister_chardev_region(dev_tfirst,unsignedintcount,char*name)静态申请设备号。Intalloc_chardev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name)动态申请设备号,注意第一个参数是传地址,而静态则是传值。3.几种重要的数据结构structfilefile结构代表一个打开的文件,它由内核在open时创建,并传递给该文件上进行操作的所有函数,直到最后的close函数。file结构private_data是跨系统调用时保存状态信息非常有用的资源。file结构的f_ops保存了文件的当前读写位置。structinode内核用inode代表一个磁盘上的文件,它和file结构不同,后者表示打开的文件描述符。对于单个文件,可能会有许多个表示打开文件的文件描述符file结构,但他们都指单个inode结构。inode的dev_ti_rdev成员包含了真正的设备编号,structcdev*i_cdev包含了指向structcdev结构的指针。structfile_operationsfile_operations结构保存了字符设备驱动程序的方法。4.字符设备的注册和注销structcdev*cdev_alloc(void);voidcdev_init(structcdev*dev,structfile_operations*fops);intcdev_add(structcdev*dev,dev_tnum,unsignedintcount);voidcdev_del(structcdev*dev);用来管理cdev结构的函数,内核中使用该结构表示字符设备。注意cdev_add函数的count参数为次设备的个数,要想拥有多个次设备,就必须将该参数设为次设备的个数。5.并发处理信号量和自旋锁的区别,使用信号量时当调用进程试图获得一个锁定了的锁时会导致进程睡眠,而自旋锁则是一直循法的等待一直到该锁解锁了为止。1)信号量DECLARE_MUTEX(name);DECLARE_MUTEX_LOCKED(name);声明和初始化用在互斥模式中的信号量的两个宏voidinit_MUTEX(structsemaphore*sem)voidinit_MUTEX_LOCKER(structsemaphore*sem);这两个函数可以在运行时初始化信号量voiddown(structsemaphore*sem);intdown_interruptible(structsemaphore*sem);intdown_trylock(structsemahpore*sem);voidup(structsemaphore*sem);锁定和解锁信号量。如果必要,down会将调用进程置于不可中断的休眠状态;相反,down_interruptible可被信号中断。down_trylock不会休眠,并且会在信号量不可用时立即返回。锁定信号量的代码最后必须使用up解锁该信号量。2)自旋锁spionlock_tlock=SPIN_LOCK_UNLOCKED;spin_lock_init(spinlock_t*lock);初始化自旋锁的两种方式。voidspin_lock(spinlock_t*lock);锁定自旋锁voidspin_unlock(spinlock_t*lock);解锁自旋锁二、驱动代码#includelinux/module.h#includelinux/types.h#includelinux/fs.h#includelinux/errno.h#includelinux/mm.h#includelinux/sched.h#includelinux/init.h#includelinux/cdev.h#includeasm/io.h#includeasm/system.h#includeasm/uaccess.h#defineMEMDEV_MAJOR251#defineMEMDEV_NUM2#defineMEMDEV_SIZE1024structmem_dev{unsignedintsize;char*data;structsemaphoresem;};staticintmem_major=MEMDEV_MAJOR;structcdevmem_cdev;structmem_dev*mem_devp;staticintmem_open(structinode*inode,structfile*filp){structmem_dev*dev;unsignedintnum;printk(mem_open.\n);num=MINOR(inode-i_rdev);//获得次设备号if(num(MEMDEV_NUM-1))//检查次设备号有效性return-ENODEV;dev=&mem_devp[num];filp-private_data=dev;//将设备结构保存为私有数据return0;}staticintmem_release(structinode*inode,structfile*filp){printk(mem_release.\n);return0;}staticssize_tmem_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos){intret=0;structmem_dev*dev;unsignedlongp;unsignedlongcount;printk(mem_read.\n);dev=filp-private_data;//获得设备结构count=size;p=*ppos;//检查偏移量和数据大小的有效性if(pMEMDEV_SIZE)return0;if(count(MEMDEV_SIZE-p))count=MEMDEV_SIZE-p;if(down_interruptible(&dev-sem))//锁定互斥信号量return-ERESTARTSYS;//读取数据到用户空间if(copy_to_user(buf,dev-data+p,count)){ret=-EFAULT;printk(copyfromuserfailed\n);}else{*ppos+=count;ret=count;printk(read%dbytesfromdev\n,count);}up(&dev-sem);//解锁互斥信号量returnret;}staticssize_tmem_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos)//注意:第二个参数和read方法不同{intret=0;structmem_dev*dev;unsignedlongp;unsignedlongcount;printk(mem_write.\n);dev=filp-private_data;count=size;p=*ppos;if(pMEMDEV_SIZE)return0;if(count(MEMDEV_SIZE-p))count=MEMDEV_SIZE-p;if(down_interruptible(&dev-sem))//锁定互斥信号量return-ERESTARTSYS;if(copy_from_user(dev-data+p,buf,count)){ret=-EFAULT;printk(copyfromuserfailed\n);}else{*ppos+=count;ret=count;printk(write%dbytestodev\n,count);}up(&dev-sem);//解锁互斥信号量returnret;}staticloff_tmem_llseek(structfile*filp,loff_toffset,intwhence){intnewpos;printk(mem_llseek.\n);switch(whence){case0:newpos=offset;break;case1:newpos=filp-f_pos+offset;break;case2:newpos=MEMDEV_SIZE-1+offset;break;default:return-EINVAL;}if((newpos0)||(newpos(MEMDEV_SIZE-1)))return-EINVAL;filp-f_pos=newpos;returnnewpos;}staticconststructfile_operationsmem_fops={.owner=THIS_MODULE,.open=mem_open,.write=mem_write,.read=mem_read,.release=mem_release,.llseek=mem_llseek,};staticint__initmemdev_init(void){intresult;interr;inti;//申请设备号dev_tdevno=MKDEV(mem_major,0);if(mem_major)result=register_chrdev_region(devno,MEMDEV_NUM,memdev);//注意静态申请的dev_t参数和动态dev_t参数的区别else{//静态直接传变量,动态传变量指针result=alloc_chrdev_region(&devno,0,MEMDEV_NUM,memdev);mem_major=MAJOR(devno);}if(result0){printk(can'tgetmajordevno:%d\n,mem_major);returnresult;}//注册设备驱动cdev_init(&mem_cdev,&mem_fops);mem_cdev.owner=THIS_MODULE;err=cdev_add(&mem_cdev,MKDEV(mem_major,0),MEMDEV_NUM);//如果有N个设备就要添加N个设备号if(err)printk(addcdevfaild,erris%d\n,err);//分配设备内存mem_devp=kmalloc(MEMDEV_NUM*(sizeof(structmem_dev)),GFP_KERNEL);if(!mem_devp){result=-ENOMEM;gotofail_malloc;}memset(mem_devp,0,MEMDEV_NUM*(sizeof(structmem_dev)));for(i=0;iMEMDEV_NUM;i++){mem_devp[i].size=MEMDEV_SIZE;mem_devp[i].data=kmalloc(MEMDEV_SIZE,GFP_KERNEL);memset(mem_devp[i].data,0,MEMDEV_SIZE);init_MUTEX(&mem_devp[i].sem);//初始化互斥锁}returnresult;fail_malloc:unregister_chrdev