/** a simple char device driver: globalmem without mutex
*
* Copyright (C) 2014 Barry Song (baohua@kernel.org)
*
* Licensed under GPLv2 or later.*/#include#include#include#include#include#include
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 230
static int globalmem_major =GLOBALMEM_MAJOR;
module_param(globalmem_major,int, S_IRUGO);structglobalmem_dev {structcdev cdev;
unsignedcharmem[GLOBALMEM_SIZE];
};struct globalmem_dev *globalmem_devp;static int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data =globalmem_devp;return 0;
}static int globalmem_release(struct inode *inode, struct file *filp)
{return 0;
}static long globalmem_ioctl(struct file *filp, unsigned intcmd,
unsignedlongarg)
{struct globalmem_dev *dev = filp->private_data;switch(cmd) {caseMEM_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 *filp, char __user *buf, size_t size,
loff_t*ppos)
{
unsignedlong p = *ppos;
unsignedint count =size;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 (copy_to_user(buf, dev->mem +p, count)) {
ret= -EFAULT;
}else{*ppos +=count;
ret=count;
printk(KERN_INFO"read %u bytes(s) from %lu", count, p);
}returnret;
}static ssize_t globalmem_write(struct file *filp, const char __user *buf,
size_t size, loff_t*ppos)
{
unsignedlong p = *ppos;
unsignedint count =size;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 (copy_from_user(dev->mem +p, buf, count))
ret= -EFAULT;else{*ppos +=count;
ret=count;
printk(KERN_INFO"written %u bytes(s) from %lu", count, p);
}returnret;
}static loff_t globalmem_llseek(struct file *filp, loff_t offset, intorig)
{
loff_t ret= 0;switch(orig) {case 0:if (offset < 0) {
ret= -EINVAL;break;
}if ((unsigned int)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;break;
}returnret;
}static const struct file_operations globalmem_fops ={
.owner=THIS_MODULE,
.llseek=globalmem_llseek,
.read=globalmem_read,
.write=globalmem_write,
.unlocked_ioctl=globalmem_ioctl,
.open=globalmem_open,
.release=globalmem_release,
};static void globalmem_setup_cdev(struct globalmem_dev *dev, intindex)
{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);
}static int __init globalmem_init(void)
{intret;
dev_t devno= MKDEV(globalmem_major, 0);if(globalmem_major)
ret= register_chrdev_region(devno, 1, "globalmem");else{
ret= alloc_chrdev_region(&devno, 0, 1, "globalmem");
globalmem_major=MAJOR(devno);
}if (ret < 0)returnret;
globalmem_devp= kzalloc(sizeof(structglobalmem_dev), GFP_KERNEL);if (!globalmem_devp) {
ret= -ENOMEM;gotofail_malloc;
}
globalmem_setup_cdev(globalmem_devp,0);return 0;
fail_malloc:
unregister_chrdev_region(devno,1);returnret;
}
module_init(globalmem_init);static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major,0), 1);
}
module_exit(globalmem_exit);
MODULE_AUTHOR("Barry Song ");
MODULE_LICENSE("GPL v2");