#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>#define GLOBALMEM_SIZE 0x1000#define MEM_CLEAR 0x1#define GLOBALMEM_MAJOR 254static int globalmem_major = GLOBALMEM_MAJOR;struct globalmem_dev{ struct cdev cdev;/*************************************** struct cdev { struct kobject koj; //内嵌的kobiject对象 struct module *owner;//所属模块 struct file_operations *ops;//文件操作结构体 struct list_head list; dev_t dev;//设备号 unsigned int count; };******************************************/ unsigned char mem[GLOBALMEM_SIZE]; };struct globalmem_dev *globalmem_devp;int globalmem_open(struct inode *inode,struct file *filep){ filep->private_data=globalmem_devp; return 0;}int globalmem_release(struct inode *inode,struct file *filep){ return 0;}static int globalmem_ioctl(struct inode *inodep,struct file *filep,unsigned int cmd,unsigned long arg){ struct globalmem_dev *dev=filep->private_data; switch(cmd) { case MEM_CLEAR: memset(dev->mem,0,GLOBALMEM_SIZE); printk(KERN_INFO "globalmem is set to zero"); break; default: return -EINVAL; } return 0;}static ssize_t globalmem_read(struct file *filep,char __user *buf,size_t size,loff_t *ppos){ unsigned long p=*ppos; unsigned int count=size; int ret=0; struct globalmem_dev *dev=filep->private_data; if(p>=GLOBALMEM_SIZE) //要读的偏移位置越界 return count ? - ENXIO:0; if(count>GLOBALMEM_SIZE-p) //要读的字节数太大count= GLOBALMEM_SIZE-p;/*内核空间—>用户空间:将内核空间中首地址为dev->mem+p的count个字节拷贝到用户空间的首地址为buf的字符串里面去*/ if(copy_to_user(buf,(void *)(dev->mem+p),count)) ret = -EFAULT; else { *ppos+=count; ret=count; printk(KERN_INFO "read %d bytes(s) from %d\n",(int)count,(int)p); } return ret;}static ssize_t globalmem_write(struct file *filep,const char __user *buf,size_t size,loff_t *ppos){ unsigned long p=*ppos; unsigned int count =size; int ret =0; struct globalmem_dev *dev=filep->private_data; if(p>=GLOBALMEM_SIZE) //要写的偏移位置越界 return count ? -ENXIO:0; if(count>GLOBALMEM_SIZE-p) //要写的字节数太大 count=GLOBALMEM_SIZE-p; /*用户空间->内核空间:将用户空间中首地址为buf的count个字节拷贝到内核空间首地址为dev-mem+p的存储空间去*/ if(copy_from_user(dev->mem+p,buf,count)) ret=-EFAULT; else { *ppos+=count; ret=count; printk(KERN_INFO "written %d bytes from %d\n",(int)count,(int)p); } return ret;}static loff_t globalmem_llseek(struct file *filep,loff_t offset,int orig){ loff_t ret=0; switch(orig) { case 0: /*从文件开头开始偏移*/ if(offset<0) { ret = -EINVAL; break; } if((unsigned int)offset>GLOBALMEM_SIZE) //偏移越界 { ret = -EINVAL; break; } filep->f_pos=(unsigned int)offset; ret =filep->f_pos; break; case 1: /*从当前位置开始偏移*/ if(((filep->f_pos+offset)>GLOBALMEM_SIZE)||((filep->f_pos+offset)<0)) //偏移越界 { ret=-EINVAL; break; } filep->f_pos+=offset; ret=filep->f_pos; break; default: ret=-EINVAL; break; } return ret;}/*通过file_operations方法将设备类型的差异屏蔽了,这就是linux能够将所有设备都理解为文件的原因。*/static const struct file_operations globalmem_fops={ /*指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES*/ .owner=THIS_MODULE, /*llseek用来修改文件当前的读写位置,返回新位置*/ .llseek=globalmem_llseek, /*从设备中同步读取数据。读取成功返回读取的字节数。设置为NULL,调用时返回-EINVAL*/ .read=globalmem_read, /*向设备发送数据*/ .write=globalmem_write, /*提供一种执行设备特殊命令的方法*/ .ioctl=globalmem_ioctl, /*由VFS调用,当VFS打开一个文件,即建立了一个新的"struct file",之后调用open方法分配文件结构。*/ .open=globalmem_open, /*file结构释放时,将调用此指针函数,release与open相同可设置为NULL*/ .release=globalmem_release,};static void globalmem_setup_cdev(struct globalmem_dev *dev,int index){ int err,devno=MKDEV(globalmem_major,index); /*初始化cdev,并建立cdev和file_operations之间的连接*/ cdev_init(&dev->cdev,&globalmem_fops); dev->cdev.owner=THIS_MODULE; dev->cdev.ops=&globalmem_fops; /*注册设备,通常发生在驱动模块的加载函数中*/ err=cdev_add(&dev->cdev,devno,1); if(err) printk(KERN_NOTICE "Error %d adding LED%d",err,index);}/*globalmem 设备驱动模块加载函数*/int globalmem_init(void){ int result; dev_t devno=MKDEV(globalmem_major,0); /*主设备号是globalmem_major,从设备号是0,通过#define MKDEV(ma,mi) (((ma)<<MINORBITS)|(mi))宏定义生成设备号devno */ if(globalmem_major)result = register_chrdev_region(devno,1,"globalmem");/**/ else { result = alloc_chrdev_region(&devno,0,1,"globalmem"); /**/ globalmem_major = MAJOR(devno); } if(result<0) return result; globalmem_devp=kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);/* kmalloc的特殊之处在于分配的内存在物理地址上是连续的 */ if(!globalmem_devp) { result = -ENOMEM; goto fail_malloc; } memset(globalmem_devp,0,sizeof(struct globalmem_dev)); /**/ globalmem_setup_cdev(globalmem_devp,0); /*初始化并添加cdev结构体*/ return 0; fail_malloc:unregister_chrdev_region(devno,1); return result;}/*globalmem设备驱动模块卸载函数*/void globalmem_exit(void){ /*注销设备,通常发生在驱动模块的卸载函数中*/ cdev_del(&globalmem_devp->cdev); kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major,0),1);}MODULE_AUTHOR("Song Baohua");/**/MODULE_LICENSE("Dual BSD/GPL");module_param(globalmem_major,int,S_IRUGO);module_init(globalmem_init);module_exit(globalmem_exit);