开发环境:Winxp(主机)+ VisualBox +fedora9(虚拟机2.6.25.4内核)
功能: globalmem字符设备框架分析(支持2个设备),设备文件动态创建,使用信号量进行并发的控制。
#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 <linux/device.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #define GLOBALMEM_SIZE 0x1000 //global mem size #define GLOBALMEM_MAJOR 240 //major number #define MEM_CLEAR 0x1 //clear global mem static int globalmem_major=GLOBALMEM_MAJOR; static struct class *globalmem_class; struct globalmem_dev{ struct cdev cdev; unsigned char mem[GLOBALMEM_SIZE]; struct semaphore sem; }; struct globalmem_dev *globalmem_devp; static int globalmem_open(struct inode *inode,struct file *filp) { struct globalmem_dev *dev; dev=container_of(inode->i_cdev,struct globalmem_dev,cdev); filp->private_data=dev; return 0; } static int globalmem_release(struct inode *inode,struct file *filp) { return 0; } static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos) { unsigned long p=*ppos; int ret=0; struct globalmem_dev *dev=filp->private_data; if(p>GLOBALMEM_SIZE) return 0; if(count>GLOBALMEM_SIZE-p) count=GLOBALMEM_SIZE-p; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; 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",count,p); } up(&dev->sem); return ret; } static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t count,loff_t *ppos) { unsigned long p=*ppos; int ret=0; struct globalmem_dev *dev=filp->private_data; if(p>GLOBALMEM_SIZE) return 0; if(count>GLOBALMEM_SIZE-p) count=GLOBALMEM_SIZE-p; if(down_interruptible(&dev->sem)) return -ERESTARTSYS; if(copy_from_user(dev->mem+p,buf,count)) ret=-EFAULT; else{ *ppos+=count; ret=count; printk(KERN_INFO "written %d bytes(s) from %d\n",count,p); } up(&dev->sem); return ret; } static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig) { loff_t ret; switch(orig){ case 0: if(offset<0){ ret=-EINVAL; break; } if(offset>GLOBALMEM_SIZE){ ret=-EINVAL; break; } filp->f_pos=(unsigned int)offset; ret=filp->f_pos; break; case 1: 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; } return ret; } static int globalmem_ioctl(struct inode *inodep,struct file *filp,unsigned int cmd,unsigned long arg) { struct globalmem_dev *dev=filp->private_data; switch(cmd){ case MEM_CLEAR: if(down_interruptible(&dev->sem)) return -ERESTARTSYS; memset(dev->mem,0,GLOBALMEM_SIZE); up(&dev->sem); printk(KERN_INFO "globalmem is set to zero\n"); break; default: return -EINVAL; } return 0; } static const struct file_operations globalmem_fops={ .owner=THIS_MODULE, .llseek=globalmem_llseek, .read=globalmem_read, .write=globalmem_write, .ioctl=globalmem_ioctl, .open=globalmem_open, .release=globalmem_release, }; static void globalmem_setup_cdev(struct globalmem_dev *dev,int index) { int err,devno=MKDEV(globalmem_major,index); cdev_init(&dev->cdev,&globalmem_fops); dev->cdev.owner=THIS_MODULE; err=cdev_add(&dev->cdev,devno,1); if(err) printk(KERN_NOTICE "ERROR %d adding globalmem %d",err,index); } int globalmem_init(void) { int result; dev_t devno=MKDEV(globalmem_major,0); if(globalmem_major) result=register_chrdev_region(devno,2,"globalmem"); else{ result=alloc_chrdev_region(&devno,0,2,"globalmem"); globalmem_major=MAJOR(devno); } if(result<0) return result; globalmem_devp=kmalloc(2*sizeof(struct globalmem_dev),GFP_KERNEL); if(!globalmem_devp){ result=-ENOMEM; goto fail_malloc; } memset(globalmem_devp,0,2*sizeof(struct globalmem_dev)); globalmem_setup_cdev(&globalmem_devp[0],0); globalmem_setup_cdev(&globalmem_devp[1],1); init_MUTEX(&globalmem_devp->sem); globalmem_class=class_create(THIS_MODULE,"globalmem"); if(IS_ERR(globalmem_class)) { printk(KERN_INFO " create class faild!\n"); return -1; } class_device_create(globalmem_class, NULL, MKDEV(globalmem_major, 0),NULL,"globalmem%d",0); class_device_create(globalmem_class, NULL, MKDEV(globalmem_major, 1),NULL,"globalmem%d",1); return 0; fail_malloc: unregister_chrdev_region(devno,1); return result; } void globalmem_exit(void) { cdev_del(&globalmem_devp[0].cdev); cdev_del(&globalmem_devp[1].cdev); kfree(globalmem_devp); unregister_chrdev_region(MKDEV(globalmem_major,0),2); class_device_destroy(globalmem_class, MKDEV(globalmem_major, 0)); class_device_destroy(globalmem_class, MKDEV(globalmem_major, 1)); class_destroy(globalmem_class); } module_param(globalmem_major,int,S_IRUGO); module_init(globalmem_init); module_exit(globalmem_exit); MODULE_LICENSE("GPL");