基于scull结构的驱动模块源码

本文主要给出完整代码,并在关键代码处标注必要说明,基础知识要点请详细察看中国电力出版社《LINUX设备驱动程序》

首先看必要的头文件asdf.h:

#ifndef _ASDF_H_
#define _ASDF_H_

#ifndef ASDF_MAJOR
#define ASDF_MAJOR 0   /* dynamic major by default */
#endif

#ifndef ASDF_MINOR
#define ASDF_MINOR 0   /* dynamic major by default */
#endif

#ifndef ASDF_DEVS
#define ASDF_DEVS 4    /* asdf0 through asdf3 */
#endif

#ifndef ASDF_QUANTUM
#define ASDF_QUANTUM 4000
#endif

#ifndef ASDF_QSET
#define ASDF_QSET    1000
#endif

#include <linux/ioctl.h>
#include <linux/cdev.h>


static int asdf_open(struct inode *inode,struct file *filp);
static int asdf_release(struct inode *inode,struct file *filp);
static ssize_t asdf_read(struct file *filp,char *buf,size_t count,loff_t *f_pos);
static ssize_t asdf_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos);
static loff_t asdf_lseek(struct file *file,loff_t offset,int orig);
static int asdf_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
/*static int asdf_mmap(struct file *, struct vm_area_struct *);
*/
struct file_operations asdf_fops={
 .owner=   THIS_MODULE,
 .open=    asdf_open,
    .release=   asdf_release,
 .read=    asdf_read,
 .write=   asdf_write,
 .llseek=  asdf_lseek,
 .ioctl=   asdf_ioctl,
 /*.mmap=    asdf_mmap,*/          
        };
struct asdf_qset {
 void **data;
 struct asdf_qset *next;
};
      
struct asdf_dev {                //自定义的数据结构

struct asdf_qset *data;
 struct asdf_dev *next;         
 int vmas;                            //active mapping
 int quantum;                     //current size of quantu
 int qset;                             //current array size
 int order;                            //current allocation order
 size_t size;                        //32 bits will suffice
 struct semaphore  sem;   //Mutual exclusion
 struct cdev cdev;
};

int asdf_trim(struct asdf_dev *dev);                    
 

extern int asdf_major;
extern int asdf_devs;
extern int asdf_quantum;
extern int asdf_qset;

/*
 * Ioctl definitions
 */

/* Use 'k' as magic number */
#define ASDF_IOC_MAGIC  'k'
/* Please use a different 8-bit number in your code */

#define ASDF_IOCRESET    _IO(ASDF_IOC_MAGIC, 0)

/*
 * S means "Set" through a ptr,
 * T means "Tell" directly with the argument value
 * G means "Get": reply by setting through a pointer
 * Q means "Query": response is on the return value
 */
#define ASDF_IOCSQUANTUM  _IOW(ASDF_IOC_MAGIC,  1, int)      /*设定9个ioctl控制参数,对quantum和*/
#define ASDF_IOCSQSET     _IOW(ASDF_IOC_MAGIC,  2, int)            /*qset的值设定与读取*/
#define ASDF_IOCTQUANTUM  _IO(ASDF_IOC_MAGIC,   3)
#define ASDF_IOCTQSET     _IO(ASDF_IOC_MAGIC,   4)
#define ASDF_IOCGQUANTUM  _IOR(ASDF_IOC_MAGIC,  5, int)
#define ASDF_IOCGQSET     _IOR(ASDF_IOC_MAGIC,  6, int)
#define ASDF_IOCQQUANTUM  _IO(ASDF_IOC_MAGIC,   7)
#define ASDF_IOCQQSET     _IO(ASDF_IOC_MAGIC,   8)

#define ASDF_IOC_MAXNR 8

#endif /* _ASDF_H_ */

以下是主要代码asdf.c:

#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
                                                                               
#include <linux/config.h>
#include <linux/module.h>
                                                                               
MODULE_LICENSE("GPL");
#ifdef CONFIG_SMP
#define __SMP__
#endif

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <asm/uaccess.h>

#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/fcntl.h>
#include <linux/aio.h>

#include "asdf.h"

int asdf_major =   ASDF_MAJOR;
int asdf_minor =   ASDF_MINOR;
int asdf_devs  =   ASDF_DEVS;

int asdf_quantum = ASDF_QUANTUM;
int asdf_qset =    ASDF_QSET;

struct asdf_dev *asdf_devices;

static void asdf_setup_cdev(struct asdf_dev *dev, int index)
{
 int err, devno = MKDEV(asdf_major, asdf_minor + index);
   
 cdev_init(&dev->cdev, &asdf_fops);
 dev->cdev.owner = THIS_MODULE;
 dev->cdev.ops = &asdf_fops;                                          /*登记fileoperation接口函数*/
 err = cdev_add (&dev->cdev, devno, 1);
 /* Fail gracefully if need be */
 if (err)
  printk(KERN_NOTICE "Error %d adding asdf%d", err, index);
}

int asdf_trim(struct asdf_dev *dev)
{
 struct asdf_qset *next, *dptr;
 int qset = dev->qset;   /* "dev" is not-null */
 int i;

 for (dptr = dev->data; dptr; dptr = next) { /* all the list items 察看struct asdf_qset链表上的数据 */
  if (dptr->data) {                                          /*如果有数据*/
   for (i = 0; i < qset; i++)                            /*释放空间*/
    kfree(dptr->data[i]);
   kfree(dptr->data);
   dptr->data = NULL;
  }
  next = dptr->next;
  kfree(dptr);
 }
 dev->size = 0;
 dev->quantum = asdf_quantum;          /*定义了asdf_qset中内存分配形式的初始值quantum,qset*/
 dev->qset = asdf_qset;
 dev->data = NULL;
 return 0;
}

struct asdf_qset *asdf_follow(struct asdf_dev *dev, int n)                /*n表示链表中有多少个struct asdf_qset*/
{
 struct asdf_qset *qs = dev->data;

        /* Allocate first qset explicitly if need be */
 if (! qs) {                                                                        /*如果为空的处理*/
  qs = dev->data = kmalloc(sizeof(struct asdf_qset), GFP_KERNEL);
  if (qs == NULL)
   return NULL;  /* Never mind */
  memset(qs, 0, sizeof(struct asdf_qset));
 }

 /* Then follow the list */                                /*遍历整个链表,如果struct asdf_qset为空,进行初始化*/
 while (n--) {                                                     /*遍历n-1次*,从struct asdf_dev传下来的struct asdf_qset开始*/
  if (!qs->next) {                                                /*如果为空,初始化,设空值*/
   qs->next = kmalloc(sizeof(struct asdf_qset), GFP_KERNEL);
   if (qs->next == NULL)
    return NULL;                                               /* 若此为最后一个struct asdf_qset,函数返回 Never mind */
   memset(qs->next, 0, sizeof(struct asdf_qset));
  }
  qs = qs->next;                                            /*若不为空的时候将qs指针值设为指向下一个struct qset的指针*/
  continue;
 }
 return qs;                                                    /*返回指向最后一个struct asdf_qset的指针*/
}


 int asdf_init_module(void)
{
 int result,i;
 dev_t dev;
 
 if(asdf_major){
 dev = MKDEV(asdf_major,asdf_minor);
 result = register_chrdev_region(dev,asdf_devs,"asdf");                          /*2.6内核模块注册方法*/
  }
 else{
 result = alloc_chrdev_region(&dev,asdf_minor,asdf_devs,"asdf");
 asdf_major = MAJOR(dev);
 }
 if(result < 0)
 return result;
 
 asdf_devices = kmalloc(asdf_devs*sizeof(struct asdf_dev),GFP_KERNEL);
 if(!asdf_devs){
  result = -ENOMEM;
  goto fail_malloc;
 }
 memset(asdf_devices,0,asdf_devs*sizeof(struct asdf_dev));
  /* initalize each devices*/
 for(i=0;i<asdf_devs;i++)
 { 
  asdf_devices[i].quantum = asdf_quantum;
  asdf_devices[i].qset = asdf_qset;
  init_MUTEX(&asdf_devices[i].sem);
  asdf_setup_cdev(&asdf_devices[i], i);
 }
 return 0;       /* succeed */

  fail_malloc:
 unregister_chrdev_region(dev,asdf_devs);
 return result;
}

 

 void asdf_cleanup_module(void)
{
 int i;
 dev_t devno = MKDEV(asdf_major, asdf_minor);

 /* Get rid of our char dev entries */
 if (asdf_devices) {
  for (i = 0; i < asdf_devs; i++) {
   /*now we clean the data source in struct asdf_dev -> strcut asdf_qet -> data*/
   asdf_trim(asdf_devices + i);
   cdev_del(&asdf_devices[i].cdev);
  }
  kfree(asdf_devices);
 }

 

 /* cleanup_module is never called if registering failed */
 unregister_chrdev_region(devno, asdf_devs);

 /* and call the cleanup functions for friend devices */
 //asdf_p_cleanup();
 //asdf_access_cleanup();
}

static int asdf_open(struct inode *inode,struct file *filp)
{
 struct asdf_dev *dev;
 /*find the device */
 dev = container_of(inode->i_cdev,struct asdf_dev ,cdev);                        /*这里注意以下2.6内核模块注册的*/
 filp->private_data = dev;                   /* for other methods */                        /*方式,与2.4所用的函数有家大区别*/

 /* now trim to 0 the length of the device if open was write-only */
 if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {
  if (down_interruptible(&dev->sem))
   return -ERESTARTSYS;
  asdf_trim(dev); /* ignore errors */
  up(&dev->sem);
 }       
 printk("^_^:open %s/n",current->comm);
 return 0;           /* success */
}

static int asdf_release(struct inode *inode,struct file *filp)
{
 printk("^_^:close/n");
 return 0;
}

static ssize_t asdf_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
 struct asdf_dev *dev = filp->private_data;
 struct asdf_qset *dptr;    /*frist  linking chain*/ 
 int quantum = dev->quantum;
 int qset = dev->qset;
 int itemsize = quantum*qset;    /*how many byte in this linking chain*/
 int item ,rest;
 int s_pos,q_pos;
 ssize_t retval = 0;
 
 if(down_interruptible(&dev->sem))
  return -ERESTARTSYS;
 if(*f_pos>=dev->size)
 goto out;
 if(*f_pos+count > dev->size)
  count = dev->size - *f_pos;
 
 item = (long)*f_pos / itemsize;
 rest = (long)*f_pos % itemsize;
 s_pos = rest / quantum;
 q_pos = rest % quantum;
 
 dptr = asdf_follow(dev,item);                                                          /*定位f_pos指针所在的struct asdf_qset*/
 
 if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
  goto out; /* don't fill holes */

 /* read only up to the end of this quantum */
 if (count > quantum - q_pos)
  count = quantum - q_pos;

 if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count)) {           /*精确定位,并拷贝数据送到用户空间*/
  retval = -EFAULT;
  goto out;
 }
 *f_pos += count;
 retval = count;

  out:
 up(&dev->sem);
 return retval;
 
}

static ssize_t asdf_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos)
{
 
 struct asdf_dev *dev = filp->private_data;
 struct asdf_qset *dptr;
 int quantum = dev->quantum, qset = dev->qset;
 int itemsize = quantum * qset;
 int item, s_pos, q_pos, rest;
 ssize_t retval = -ENOMEM; /* value used in "goto out" statements */

 if (down_interruptible(&dev->sem))
  return -ERESTARTSYS;

 /* find listitem, qset index and offset in the quantum */
 item = (long)*f_pos / itemsize;
 rest = (long)*f_pos % itemsize;
 s_pos = rest / quantum; q_pos = rest % quantum;

 /* follow the list up to the right position */
 dptr = asdf_follow(dev, item);
 if (dptr == NULL)
  goto out;
 if (!dptr->data) {
  dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
  if (!dptr->data)
   goto out;
  memset(dptr->data, 0, qset * sizeof(char *));
 }
 if (!dptr->data[s_pos]) {
  dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
  if (!dptr->data[s_pos])
   goto out;
 }
 /* write only up to the end of this quantum */
 if (count > quantum - q_pos)
  count = quantum - q_pos;

 if (copy_from_user(dptr->data[s_pos]+q_pos, buf, count)) {
  retval = -EFAULT;
  goto out;
 }
 *f_pos += count;
 retval = count;

        /* update the size */
 if (dev->size < *f_pos)
  dev->size = *f_pos;

  out:
 up(&dev->sem);
 return retval;
 
}

static loff_t asdf_lseek(struct file *filp,loff_t offset,int orig)
{
 struct asdf_dev *dev = filp->private_data;
 loff_t newpos;
 
 switch(orig){
  case 0: newpos = offset;
   break;
  case 1: newpos = filp->f_pos + offset;
   break;
  case 2: newpos = dev->size + offset;
   break;
  default:
   return -EINVAL;
  }
 if(newpos<0)  return -EINVAL;
 return filp->f_pos = newpos;
}


static int asdf_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
 int err = 0;
 int retval = 0;
/* int tmp;
*/ 
 if(_IOC_TYPE(cmd)!= ASDF_IOC_MAGIC)  return -ENOTTY;
 if(_IOC_NR(cmd)>ASDF_IOC_MAXNR)   return -ENOTTY;
 
 if(_IOC_DIR(cmd)&_IOC_READ)
  err=!access_ok(VERIFY_WRITE,(void __user*)arg,_IOC_SIZE(cmd));
 else if(_IOC_DIR(cmd)&_IOC_WRITE)
  err=!access_ok(VERIFY_READ,(void __user*)arg,_IOC_SIZE(cmd));
 if(err)
  return -EFAULT;
  
 switch(cmd){
       /*reset the value quantum and qset*/
  case ASDF_IOCRESET: asdf_quantum = ASDF_QUANTUM;
     asdf_qset = ASDF_QSET;
     break;
       /* set the value of quantum by user*/   
  case ASDF_IOCSQUANTUM: retval = __get_user(asdf_quantum,(int __user*)arg);
     break;
     
  case ASDF_IOCTQUANTUM: asdf_quantum = arg;
     break;
     
  case ASDF_IOCGQUANTUM:  retval = __put_user(asdf_quantum,(int __user*)arg);
     break;
     
  case ASDF_IOCQQUANTUM:  return asdf_quantum;
  
  /* set the value of qset by user*/   
  case ASDF_IOCSQSET: retval = __get_user(asdf_qset,(int __user*)arg);
     break;
     
  case ASDF_IOCTQSET: asdf_qset = arg;
     break;
     
  case ASDF_IOCGQSET:     retval = __put_user(asdf_qset,(int __user*)arg);
     break;
     
  case ASDF_IOCQQSET:     return asdf_qset;
  
  default:  /* redundant, as cmd was checked against MAXNR */
  return -ENOTTY;
 }
 return retval;
}
  
  
module_init(asdf_init_module);
module_exit(asdf_cleanup_module);

 Makefile文件:

obj-m   :=asdf.o
KDIR    :=/lib/modules/$(shell uname -r)/build
PWD     :=$(shell pwd)

default : 
  $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
clean:
 rm -f *.o *.ko *.mod.c *~ *.bak

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值