仅供参考
#include <linux/moducle.h>
#include <linux/types.h>
#include <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/uacccess.h>
#define VIRTUALDISK_SIZE 0x2000 //size =8K
#define MEM_CLEAR 0x1
#define PORT1_SET 0x1
#define PORT2_SET 0x2
#define VIRTUALDISK_MAJOR 200 //virtualDisk major device port = 200
#define MINORBITS 20
#define MINORMASK ((1U <<MINORBITS) -1 )
#define MAJOR(dev) ((unsigned int )((dev)>>MINORBITS))
#define MINOR(dev) ((unsigned int)((dev)&MINORMASK))
static int VirtualDisk_major = VIRTUALDISK_MAJOR;
struct VirtualDisk
{
struct cdev cdev;
unsigned char mem[VIRTUALDISK_SIZE];
int port1;
long port2;
long count; //recort the number of device is opening
};
int VirtualDisk_init(void)
{
int result;
dev_t devno = MKDEV(VirtualDisk_major,0); //build the device number
// apply the device number
if(VirtualDisk_major)
{
result = register_chrdev_region(&devno, 1, "VirtualDisk"); //if not 0, static apply
}
else
{
result = alloc_chrdev_region(&devno,0, 1, "VirtualDisk"); //auto
VirtualDisk_major = MAJOR(devno);
}
if(result < 0)
{
return result;
}
cdev Virtualdisk_devp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
if(! Virtualdisk_devp)
{
result = -ENOMEM;
goto fail_kmalloc;
}
memset(Virtualdisk_devp, 0, sizeof(struct VirtualDisk));
VitualDisk_setup_cdev(Virtualdisk_devp, 0);
return 0;
fail_kmalloc:
unregister_chrdev_region(devno, 1);
return result;
}
//module rmmod
void VirtualDisk_exit(void)
{
cdev_del(&Virtualdisk_devp->cdev);
kfree(Virtualdisk_devp);
unregister_chrdev_region(MKDEV(VirtualDisk_major,0),1);
}
static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minor)
{
int err;
devno = MKDEV(VirtualDisk_major,minor);
cdev_init(&dev->cdev, &VirtualDisk_fops);
dev-cdev.ower = THIS_MODULE;
dev->cdev.ops = &VirtualDisk_fops;
err = cdev_add(&dev->cdev, devno, 1);
if(err)
{
printk(KERN_NOTICE "Error in cdev_add()\n");
}
}
static const struct file_operations VirtualDisk_fops =
{
.owner = THIS_MODULE,
.llseek = VirtualDisk_llseek,
.read = VirtualDisk_read,
.write = VirtualDisk_write,
.ioctl = VirtualDisk_ioctl,
.open = VirtualDisk_open,
.release= VirtualDisk_release,
}
int VirtualDisk_open(struct inode *inode, struct file *filep)
{
filep->private_data = VirtualDisk_devp;
struct VirtualDisk *devp = filep->private_data;
devp->count++;
return 0;
}
int VirtualDisk_release(struct inode *inode, struct file *filep)
{
struct VirtualDisk *devp = filep->private_data;
devp->count--;
return 0;
}
static ssize_t VirtualDisk_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 VirtualDisk* devp = filep->private_data;
if(p >= VIRTUALDISK_SIZE)
{
return count?-ENXIO:0;
}
if(count > VIRTUALDISK_SIZE -p )
{
count = VIRTUALDISK_SIZE -p;
}
if(copy_to_user(buf, (void*)(devp->mem+p), count))
{
ret = -EFAULT;
}
else
{
*ppos +=count;
return = count;
printk(KERN_INFO "read %d bytes(s) from %d \n", count ,p);
}
return ret;
}
static ssize_t VirtualDisk_write (struct file *filep, const char __user *buf, size_t size , loff_t *ppos)
{
unsigned long p = *ppos;
int ret =0;
unsigned int count = size;
struct VirtualDisk *devp = filep->private_data;
if(p >= VIRTUALDISK_SIZE)
{
return count? -ENXIO:0;
}
if(count > VIRTUALDISK_SIZE - p)
{
retu VIRTUALDISK_SIZE - p ;
}
if(copy_from_user(devp->mem + p, buf, count))
{
return -EFAULT;
}
else
{
*ppos += count;
ret = count;
printk (KERN_INFO "written %d bytes(s) from %d\n", count ,p);
}
return ret;
}
static loff_t VirtualDisk_llseek(struct file *filep, loff_t offset ,int orig)
{
switch (orig)
{
case SEEK_SET:
if(offset < 0)
{
ret = -EINVAL;
break;
}
if((unsigned int )offset > VIRTUALDISK_SIZE)
{
ret = - EINVAL;
break;
}
filep->f_pos = (unsigned int )offset;
ret = filep->f_pos;
break;
case SEEK_CUR:
if((filep->f_pos + offset)>VIRTUALDISK_SIZE)
{
ret = -EINVAL;
break;
}
if((filep->f_pos) + offset < 0)
{
ret = -EINVAL;
break;
}
filep->f_pos += offset;
ret = filep->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int VirtualDisk_ioctl(struct inode *inodep, struct file* filep, unsigned int cmd, unsigned long arg)
{
struct VirtualDisk*devp = filep->private_data;
switch(cmd)
{
case EM_CLEAR:
memset(devp->mem, 0 , VIRTUALDISK_SIZE);
printk(KERN_INFO "VirtualDisk is set to zero \n");
break;
case PORT1_SET:
devp->port1 = 0;
break;
case PORT2_SET:
devp->port2 = 0;
break;
default:
return -EINVAL;
}
return 0;
}