#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/slab.h> /**void *kmalloc(size_t size,int flags); */ #include <linux/cdev.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> //#include <asm/semaphore.h> #define MEMDEV_MAJOR 251 #define MEMDEV_NUM 2 #define MEMDEV_SIZE 1024 struct mem_dev { unsigned int size; char *data; struct semaphore sem; }; static int mem_major = MEMDEV_MAJOR; struct cdev mem_cdev; struct mem_dev *mem_devp; static int mem_open(struct inode *inode,struct file *filp) { struct mem_dev *dev; unsigned int num; printk("mem_open.\n"); num = MINOR(inode->i_rdev);//获得次设备号 if(num> (MEMDEV_NUM -1)) //检查次设备号有效性 { return -ENODEV; } dev= &mem_devp[num]; filp->private_data= dev; //将设备结构保存为私有数据 return 0; } static int mem_release(struct inode *inode,struct file *filp) { printk("mem_release.\n"); return 0; } static ssize_t mem_read(struct file *filp,char __user *buf, size_t size, loff_t *ppos) { int ret = 0; struct mem_dev *dev; unsigned long p; unsigned long count; printk("mem_read.\n"); dev= filp->private_data;//获得设备结构 count= size; p= *ppos; //检查偏移量和数据大小的有效性 if(p> MEMDEV_SIZE) return 0; 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("copyfrom user failed\n"); } else{ *ppos+= count; ret= count; printk("read %ld bytes from dev\n", count); } up(&dev->sem);//解锁互斥信号量 return ret; } static ssize_t mem_write(struct file *filp,const char __user *buf, size_t size, loff_t *ppos)//注意:第二个参数和read方法不同 { int ret = 0; struct mem_dev *dev; unsigned long p; unsigned long count; printk("mem_write.\n"); dev= filp->private_data; count= size; p= *ppos; if(p> MEMDEV_SIZE) return 0; 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("copyfrom user failed\n"); } else{ *ppos+= count; ret= count; printk("write%ld bytes to dev\n", count); } up(&dev->sem);//解锁互斥信号量 return ret; } static loff_t mem_llseek(struct file *filp,loff_t offset, int whence) { int newpos; printk("mem_llseek.\n"); switch(whence) { case 0: newpos= offset; break; case 1: newpos= filp->f_pos + offset; break; case 2: newpos= MEMDEV_SIZE - 1 + offset; break; default: return -EINVAL; } if((newpos<0)|| (newpos>(MEMDEV_SIZE - 1))) return -EINVAL; filp->f_pos= newpos; return newpos; } static const struct file_operations mem_fops = { .owner= THIS_MODULE, .open= mem_open, .write= mem_write, .read= mem_read, .release= mem_release, .llseek= mem_llseek, }; static int __init memdev_init(void) { int result; int err; int i; //申请设备号 dev_t devno = 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(result< 0){ printk("can'tget major devno:%d\n", mem_major); return result; } //注册设备驱动 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("addcdev faild,err is %d\n", err); //分配设备内存 mem_devp= kmalloc(MEMDEV_NUM*(sizeof(struct mem_dev)), GFP_KERNEL); if(!mem_devp){ result = - ENOMEM; goto fail_malloc; } memset(mem_devp,0, MEMDEV_NUM*(sizeof(struct mem_dev))); for(i=0;i<MEMDEV_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); sema_init(&mem_devp[i].sem,1);//初始化互斥锁 } return result; fail_malloc: unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM); return result; } static void memdev_exit(void) { cdev_del(&mem_cdev); unregister_chrdev_region(MKDEV(mem_major,0), MEMDEV_NUM);//注意释放的设备号个数一定要和申请的设备号个数保存一致 //否则会导致设备号资源流失 printk("memdev_exit\n"); } module_init(memdev_init); module_exit(memdev_exit); MODULE_AUTHOR("Y-Kee"); MODULE_LICENSE("GPL");