Chinaunix首页 | 论坛 | 博客
  • 博客访问: 201619
  • 博文数量: 71
  • 博客积分: 3000
  • 博客等级: 中校
  • 技术积分: 450
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-29 21:28
个人简介

思考人生、专注技术

文章分类

全部博文(71)

文章存档

2015年(1)

2009年(2)

2008年(11)

2007年(57)

我的朋友

分类: 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      /*kmalloc*/
#include    /*container_of*/
#include    /*memset*/
#include     /*copy_to_user*/
#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


阅读(858) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~