您的位置:首页 > 运维架构 > Linux

Linux内核开发之简单字符设备驱动(上)

2010-10-05 18:46 246 查看
废话少说,先来介绍几个必须要知道的和字符设备有关的结构体,然后结合代码详细讲解。
第一部分必要的设备结构体
1)linux2.6内核中使用cdev结构体表示字符设备:
structcdev
{
structkobjectkobj;//内嵌的kobject对象
structmodule*owner;//所属模块
structfile_operations*ops;//文件操作结构体
structlist_headlist;
dev_tdev;//设备号,长度为32位,其中高12为主设备号,低20位为此设备号
unsignedintcount;
};
可以使用下列宏从dev_t中获得主次设备号:也可以使用下列宏通过主次设备号生成dev_t:MAJOR(dev_tdev);MKDEV(intmajor,intminor);MINOR(dev_tdev);说明:在2.6内核中可以容纳大量的设备,而先前的内核版本却限于255个主设备号和255个此设备号。2)file_operations结构体中的成员函数是字符设备驱动程序设计中的主体内容,这些函数实际会在应用程序进行linux的open(),write(),read(),close()等系统调用时被最终调用。目前的file_operations结构已经变得非常大,在这里我们就关心和我这个设备程序有关的几个函数,以后用到了,咱们再提也不迟:
staticconststructfile_operationsglobalmem_fops=
{
.owner=THIS_MODULE,
.llseek=globalmem_llseek,//修改一个文件的当前读写位置并将新位置返回,出错时,返回一个负值
.read=globalmem_read,
.write=globalmem_write,
.ioctl=globalmem_ioctl,//设备相关控制命令的实现,内核可以识别一部分控制命令(这时就不用调用ioctl
()),如果设备不提供这个函数,而内核又不识别该命令,则返回-EINVAL.
.open=globalmem_open,
.release=globalmem_release,
};
3)file结构与用户空间的File没有任何关联,strcutfile是一个内核结构,它不会出现在用户程序中。它代表一个打开的文件(不局限于设备驱动程序,系统中每个打开的文件在内核空间中都有一个对应的file结构)。它由内核在open时创建,并传递给在该文件进行操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会释放掉这个数据结构。4)内核用它node结构在内部表示文件,其和file结构不同。后者表示打开的文件描述符。对于单个文件,可能会有很多表示打开的文件描述符的file结构,但它们都指向单个inode结构。在它里边和我们驱动程序有用的字段只有两个:dev_ti_rdev;//对表示设备文件的inode结构,该字段包含了真正的设备编号structcdev*i_cdev;//是表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向structcdev结构的指针。我么可以使用下边两个宏从inode中获得主设备号和此设备号:
unsignedintiminor(structinode*inode);unsignedintimajor(structinode*inode);
为了程序的可移植性,我们应该使用上述宏,而不是直接操作i_rdev。第二部分源代码详解
必要的头文件#include<linux/module.h>#include<linux/types.h>#include<linux/fs.h>#include<linux/errno.h>#include<linux/mm.h>#include<linux/sched.h>#include<linux/init.h>#include<linux/cdev.h>#include<asm/io.h>#include<asm/system.h>#include<asm/uaccess.h>
#defineGLOBALMEM_SIZE0X1000/*全局内存大小4kb*/
#defineMEM_CLEAR0x1//清零全局内存
#defineGLOBALMEM_MAJOR150//预设的globalmem的主设备号
staticglobalmem_major=GLOBALMEM_MAJOR;
//globalmem设备结构体structglobalmem_dev{structcdevcdev;unsignedcharmem[GLOBALMEM_SIZE];//全局内存};
structglobalmem_dev*globalmem_devp;//设备结构指针//文件打开函数intglobalmem_open(structinode*inode,structfile*filp){filp->private_data=globalmem_devp;return0;}//文件释放函数intglobalmem_release(structinode*inode,structfile*filp){return0;}//globalmem_ioctl函数staticintglobalmem_ioctl(structinode*inodep,structfile*filp,unsignedintcmd,unsignedlongarg){structglobalmem_dev*dev=filp->private_data;switch(cmd){caseMEM_CLEAR://清除全局内存memset(dev->mem,0,GLOBALMEM_SIZE);printk(KERN_INFO"globalmemissettozero\n");break;default:return-EINVAL;//其他不支持的命令}return0;}//globalmem_read函数staticssize_tglobalmem_read(structfile*filp,char__user*buf,size_tsize,loff_t*ppos){unsignedlongp=*ppos;unsignedintcount=size;intret=0;structglobalmem_dev*dev=filp->private_data;//获得设备结构指针//分析和获取有效的写长度if(p>=GLOBALMEM_SIZE)returncount?-ENXIO:0;if(count>GLOBALMEM_SIZE-p)//如果要求读取的比实际可用的少count=GLOBALMEM_SIZE-p;if(copy_to_user(buf,(void*)(dev->mem+p),count)){ret=-EFAULT;}else{*ppos+=count;ret=count;printk(KERN_INFO"read%dbyte(s)from%d",count,p);}returnret;}//globalmem_writestaticssize_tglobalmem_write(structfile*filp,constchar__user*buf,size_tsize,loff_t*ppos){unsignedlongp=*ppos;unsignedintcount=size;intret=0;structglobalmem_dev*dev=filp->private_data;//获得设备结构指针//分析和获取有效的写长度if(p>=GLOBALMEM_SIZE)returncount?-ENXIO:0;if(count>GLOBALMEM_SIZE-p)//如果要求读取的比实际可用的少count=GLOBALMEM_SIZE-p;if(copy_from_user(dev->mem+p,buf,count))ret=-EFAULT;else{*ppos+=count;ret=count;printk(KERN_INFO"written%dbytes(s)from%d\n",count,p);}returnret;}//globalmem_seek函数staticloff_tglobalmem_llseek(structfile*filp,loff_toffset,intorig){loff_tret=0;switch(orig){case0://从文件开头开始偏移if(offset<0){ret=-EINVAL;break;}if((unsignedint)offset>GLOBALMEM_SIZE)//偏移越界{ret=-EINVAL;break;}filp->f_pos=(unsignedint)offset;ret=filp->f_pos;break;case1://从当前位置偏移if((filp->f_pos+offset)>GLOBALMEM_SIZE)//偏移越界{ret=-EINVAL;break;}if((filp->f_pos+offset)<0){ret=-EINVAL;break;}filp->f_pos+=offset;ret=filp->f_pos;break;default:ret=-EINVAL;break;}returnret;}
以上介绍了Linux简单字符设备中涉及到的基本而要特别重要的数据结构,还有就是源代码部分中有关file_operations中的所有操作,这些都将在应用程序进行Linux的open(),write(),read(),close()等系统调用时最终被调用。这些都是从源代码中直接copy出来的,无论顺序还是结构都保持了实际代码的完整性,可以和下篇部分完整拷贝下来实际测试使用。

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: