#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#define MEMDEV_SIZE 0x1000
#define MEM_CLEAR 0x1
#define MEMDEV_MAJOR 250
#define MEMDEV_NAME "memdev"
static int memdev_major=MEMDEV_MAJOR;
struct memdev_dev
{
struct cdev cdev;
unsigned char mem[0x1000];
struct semaphore sem;
};
struct memdev_dev *memdev_devp;
static int memdev_open(struct inode *inode,struct file *filp)
{
struct memdev_dev *dev;
dev = container_of(inode->i_cdev,struct memdev_dev,cdev);
filp->private_data = dev;
return 0;
}
int memdev_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t memdev_read(struct file *filp,char __user *buf,
size_t size,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct memdev_dev *dev=filp->private_data;
if(p>=MEMDEV_SIZE)
{
return count ? -ENXIO:0;
}
if(count>MEMDEV_SIZE-p)
count=MEMDEV_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 from %ld \n",count,p);
}
up(&dev->sem);
return ret;
}
static ssize_t memdev_write(struct file *filp,const char __user *buf,size_t size,
loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct memdev_dev *dev=filp->private_data;
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"writen %d bytes from %ld\n",count,p);
}
up(&dev->sem);
return ret;
}
static loff_t memdev_llseek(struct file *filp,loff_t offset,int orig)
{
loff_t ret;
switch(orig)
{
case 0:
if(offset<0)
{
ret=-EINVAL;
break;
}
if((unsigned int)offset>MEMDEV_SIZE)
{
ret=-EINVAL;
break;
}
filp->f_pos=(unsigned int )offset;
ret=filp->f_pos;
break;
case 1:
if((filp->f_pos+offset)>MEMDEV_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;
}
return ret;
}
static int memdev_ioctl(struct inode *inodep,struct file *filp,unsigned
int cmd,unsigned long arg)
{
struct memdev_dev *dev=filp->private_data;
switch(cmd)
{
case MEM_CLEAR:
if(down_interruptible(&dev->sem))
{
return -ERESTARTSYS;
}
memset(dev->mem,0,MEMDEV_SIZE);
printk(KERN_INFO"memdev is set to zero!\n");
up(&dev->sem);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct file_operations memdev_fops={
.owner=THIS_MODULE,
.llseek=memdev_llseek,
.read=memdev_read,
.write=memdev_write,
.ioctl=memdev_ioctl,
.open=memdev_open,
.release=memdev_release,
};
static void memdev_setup_cdev(struct memdev_dev *dev,int index)
{
int err,devno=MKDEV(memdev_major,index);
cdev_init(&dev->cdev,&memdev_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&memdev_fops;
err=cdev_add(&dev->cdev,devno,1);
if(err)
{
printk(KERN_NOTICE"Error %d adding LED%d",err,index);
}
}
int memdev_init(void)
{
int result;
dev_t devno=MKDEV(memdev_major,0);
if(memdev_major)
{
result=register_chrdev_region(devno,2,"memdev");
}
else
{
result=alloc_chrdev_region(&devno,0,2,"memdev");
memdev_major=MAJOR(devno);
}
if(result<0)
return result;
else {
printk(MEMDEV_NAME"\t initialized\n");
}
memdev_devp=kmalloc(2*sizeof(struct memdev_dev),GFP_KERNEL);
if(!memdev_devp)
{
result=-ENOMEM;
goto fail_malloc;
}
memset(memdev_devp,0,2*sizeof(struct memdev_dev));
memdev_setup_cdev(memdev_devp,0);
init_MUTEX(&memdev_devp->sem);
return 0;
fail_malloc: unregister_chrdev_region(devno,2);
return result;
}
void memdev_exit(void)
{
cdev_del(&(memdev_devp[0].cdev));
cdev_del(&(memdev_devp[1].cdev));
kfree(memdev_devp);
unregister_chrdev_region(MKDEV(memdev_major,0),2);
}
MODULE_AUTHOR("sunflower");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);