思考人生、专注技术
分类: LINUX
2009-04-30 13:49:54
/*
there are operations about cdev in this file.
wood_dev contains a block mem, used to be read and write for users.
this file describles the IO functions about a char device.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "wooda.h"
struct wood *wood_dev = NULL; /*the manager struct of char device*/
int wood_open(struct inode *pInode,struct file *pFile);
int wood_release(struct inode *pInode,struct file *pFile);
ssize_t wood_read(struct file *pFile,char __user *buf,size_t count,loff_t *f_pos);
ssize_t wood_write(struct file *pFile,const char __user *buf,size_t count,loff_t *f_pos);
loff_t wood_llseek(struct file*,loff_t,int);
int wood_ioctl(struct inode *,struct file *,unsigned int,unsigned long);
irqreturn_t wood_irq_handler();
struct file_operations wood_ops={
.owner = THIS_MODULE,
.open = wood_open,
.release = wood_release,
.read = wood_read,
.write = wood_write,
.llseek = wood_llseek,
.ioctl = wood_ioctl,
};
/*do nothing, only to fill the member "private_data" of the file struct*/
int wood_open(struct inode *pInode,struct file *pFile)
{
struct wood *pDev = NULL;
pDev = container_of(pInode->i_cdev,struct wood,dev);
pFile->private_data = pDev;
/*alloc mem for user to read and write*/
/* if(down_interruptible(&pDev->sem))
return -EINTR; //need to tell user that open is interrupted
up(&wood_dev->sem);
*/
return 0;
}
/*do nothing*/
int wood_release(struct inode *pInode,struct file *pFile)
{
// struct wood *tmp = (struct wood*)(pFile->private_data);
return 0;
}
/*static DECLARE_WAIT_QUEUE_HEAD(readQue);
*/
/*read the count chars from the device mem*/
ssize_t wood_read(struct file *pFile,char __user *buf,size_t count,loff_t *f_pos)
{
char *tmp = NULL;
unsigned num = 0;
struct wood *woodTmp = (struct wood *)(pFile->private_data);
printk(KERN_ALERT "process");
wait_event_interruptible(woodTmp->bufMgr.writeQue,woodTmp->bufMgr.writeFlag!=0);
// down_read(&woodTmp->rwSem); /*read lock*/
down(&woodTmp->bufMgr.sem);
printk(KERN_ALERT "before read, file posion is %lld\n",pFile->f_pos);
if(woodTmp->bufMgr.writeFlag == 0) /*check again,in case before getting the semaphore,readFlag changes*/
{
up(&woodTmp->bufMgr.sem);
return -1;
}
if((unsigned int)(count+*f_pos)>woodTmp->bufMgr.maxLen)
num = woodTmp->bufMgr.maxLen-*f_pos;
else
num = count;
tmp = woodTmp->bufMgr.data+*f_pos;
if(copy_to_user(buf,tmp,num))
return -1;
*f_pos+=num;
if(*f_pos==woodTmp->bufMgr.maxLen) /*if read to the end,then set readFlag 0*/
woodTmp->bufMgr.writeFlag = 0;
printk(KERN_ALERT "after read, file posion is %lld\n",pFile->f_pos);
// up_read(&woodTmp->rwSem);
up(&woodTmp->bufMgr.sem);
return num;
}
/*write the strrings to the device mem*/
ssize_t wood_write(struct file *pFile,const char __user *buf,size_t count,loff_t *f_pos)
{
unsigned int num = 0;
struct wood *woodTmp = (struct wood *)(pFile->private_data);
char *tmp = woodTmp->bufMgr.data+*f_pos;
// downgrade_write(&woodTmp->rwSem); /*write lock*/
down(&woodTmp->bufMgr.sem);
if((unsigned int)(*f_pos+count)>woodTmp->bufMgr.maxLen)
num = woodTmp->bufMgr.maxLen-*f_pos;
else
num = count;
if(copy_from_user(tmp,buf,num))
return -1;
*f_pos+=num;
// up_write(&woodTmp->rwSem);
woodTmp->bufMgr.writeFlag = 1;
wake_up_interruptible(&woodTmp->bufMgr.writeQue);
up(&woodTmp->bufMgr.sem);
return num;
}
/*change the offset of the device file*/
loff_t wood_llseek(struct file *pFile,loff_t offset,int origin)
{
struct wood *pDev = pFile->private_data;
loff_t newPos = 0;
switch(origin)
{
case SEEK_SET:
newPos = offset;
break;
case SEEK_CUR:
newPos = pFile->f_pos+offset;
break;
case SEEK_END:
newPos = pDev->bufMgr.maxLen+offset;
break;
default:
break;
}
if(newPos<0)
return -EINVAL;
printk(KERN_ALERT "file posion is %lld\n",newPos);
return(pFile->f_pos = newPos);
}
int wood_ioctl(struct inode *pInode,struct file *pFile,unsigned int cmd,unsigned long arg)
{
struct wood *woodTmp = (struct wood*)pFile->private_data;
printk(KERN_ALERT "this is ioctl!cmd is %u\n",cmd);
printk(KERN_ALERT "this is ioctl!cmd is %u\n",WOOD_IOCRESET);
switch(cmd)
{
case WOOD_IOCRESET:
down(&woodTmp->bufMgr.sem);
if(!capable(CAP_SYS_ADMIN))
return -EPERM;
memset(woodTmp->bufMgr.data,0x5A,woodTmp->bufMgr.maxLen);
up(&woodTmp->bufMgr.sem);
break;
default:
return -ENOTTY;
}
return 0;
}
/*setup the device and initialize the membership,contains sem and mem*/
int wood_setup()
{
int error = -1;
dev_t devId = 0;
int wood_major = 0,wood_minor = 0;
/*alloc mem for device data*/
wood_dev =(struct wood*)kmalloc(sizeof(struct wood),GFP_KERNEL);
if (NULL==wood_dev)
return error;
/*alloc the device id*/
error = alloc_chrdev_region(&devId,0,1,DEVICE_NAME);
if (error<0)
{
printk(KERN_ALERT "alloc cdev id failed!\n");
alloc_chr_err:
kfree(wood_dev);
return error;
}
printk(KERN_ALERT "alloc cdev id is %d\n",devId);
wood_major = MAJOR(devId);
wood_minor = MINOR(devId);
wood_dev->id = devId;//MKDEV(wood_major,wood_minor);
printk(KERN_ALERT "cdev id is %d\n",wood_dev->id);
// return 0;
/*alloc IO port of parallel and the number of irq*/
error = request_region(0x378,8,"wood");
if(NULL==error)
{
printk(KERN_ALERT "request_region error!\n");
region_err:
unregister_chrdev_region(devId,1);
goto alloc_chr_err;
}
error = request_irq(7,wood_irq_handler,SA_INTERRUPT,"wood",NULL);
if(error)
{
printk(KERN_ALERT "request_irq error!\n");
irq_err:
release_region(0x378,8);
goto region_err;
}
/*init the device*/
wood_dev->bufMgr.maxLen = MAXLEN;
init_waitqueue_head(&wood_dev->bufMgr.writeQue);
//init the lock
init_MUTEX(&wood_dev->bufMgr.sem);
// init_rwsem(&wood_dev->rwSem);
//alloc mem for data
wood_dev->bufMgr.data = kmalloc(wood_dev->bufMgr.maxLen,GFP_KERNEL);
if(NULL==wood_dev->bufMgr.data)
{
printk(KERN_ALERT "wood_open kmalloc failed!\n");
// up(&wood_dev->sem);
kmallloc_err:
free_irq(7,&wood_dev->id);
goto irq_err;
}
memset(wood_dev->bufMgr.data,0x5A,wood_dev->bufMgr.maxLen);
cdev_init(&wood_dev->dev,&wood_ops);
// wood_dev->dev.ops = &wood_ops;
error = cdev_add(&wood_dev->dev,wood_dev->id,1);
if(error<0)
{
printk(KERN_ALERT "cdev_add failed!\n");
kfree(wood_dev->bufMgr.data);
goto kmalloc_err;
}
return 0;
}
/*unload the device and free the resoures*/
int wood_unload()
{
/*unload the device*/
cdev_del(&wood_dev->dev);
/*free the device id and mem*/
unregister_chrdev_region(wood_dev->id,1);
release_region(0x378,8);
free_irq(7,&wood_dev->id);
kfree(wood_dev->bufMgr.data);
// sema_destory(&wood_dev->sem);
kfree(wood_dev);
return 0;
}
/*int interruptible*/
irqreturn_t wood_irq_handler()
{
struct wood *wood_dev = NULL;
return 0;
}
#ifndef _WOOD_H
#define _WOOD_H
#include
#include
#include
#include
#include
#define DEVICE_NAME "wood"
#define MAXLEN 1024 /*the mem size of the device*/
/*define about ioctl*/
#define WOOD_IOC_MAGIC 'w'
#define WOOD_IOCRESET _IO(WOOD_IOC_MAGIC,0)
struct bufferMgr
{
char *data;
unsigned int maxLen;
char *read;
char *write;
struct semaphore sem; /**/
wait_queue_head_t writeQue;
unsigned char writeFlag;
};
struct wood
{
// char *sign = "This is my first driver!";
struct cdev dev; /*char dev struct*/
// struct semaphore sem; /**/
// struct rw_semaphore rwSem;
dev_t id; /*the device number*/
// char *data;
// unsigned int maxLen;
// char readFlag;
// wait_queue_head_t readQue;
struct bufferMgr bufMgr;
};
int wood_setup(void);
int wood_unload(void);
#endif