Chinaunix首页 | 论坛 | 博客
  • 博客访问: 397423
  • 博文数量: 115
  • 博客积分: 2501
  • 博客等级: 少校
  • 技术积分: 1009
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-23 17:05
文章分类

全部博文(115)

文章存档

2011年(2)

2010年(86)

2009年(27)

我的朋友

分类: 嵌入式

2010-08-07 22:44:27

/*

 * main.c -- the bare scull char module

 *

 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet

 * Copyright (C) 2001 O'Reilly & Associates

 *

 * The source code in this file can be freely used, adapted,

 * and redistributed in source or binary form, so long as an

 * acknowledgment appears in derived source files.  The citation

 * should list that the code comes from the book "Linux Device

 * Drivers" by Alessandro Rubini and Jonathan Corbet, published

 * by O'Reilly & Associates.   No warranty is attached;

 * we cannot take responsibility for errors or fitness for use.

 *

 */

//由于编译报错,这几行是我添加的

#include

#include

#include

#include

 

#include       /* printk() */

#include          /* kmalloc() */

#include             /* everything... */

#include        /* error codes */

#include        /* size_t */

#include

#include /* O_ACCMODE */

#include

#include

 

#include              /* cli(), *_flags */

#include      /* copy_*_user */

 

#include "scull.h"           /* local definitions */

 

/*scull默认的scull主、次设备号,设备数量,量子大小,数组大小*/

int scull_major =   SCULL_MAJOR;  

int scull_minor =   0;

int scull_nr_devs = SCULL_NR_DEVS;       /* number of bare scull devices */

int scull_quantum = SCULL_QUANTUM;

int scull_qset =    SCULL_QSET;

 

/*设置的模块参数:scull主、次设备号,设备数量,量子大小,数组大小。加载模块时可设置该变量的值*/

module_param(scull_major, int, S_IRUGO);

module_param(scull_minor, int, S_IRUGO);

module_param(scull_nr_devs, int, S_IRUGO);

module_param(scull_quantum, int, S_IRUGO);

module_param(scull_qset, int, S_IRUGO);

 

/*模块的作者和遵循的许可申明*/

MODULE_AUTHOR("Alessandro Rubini, Jonathan Corbet");

MODULE_LICENSE("Dual BSD/GPL");

 

struct scull_dev *scull_devices;    /* 模块的scull_dev数据结构,将会在模块初始化中分配内存*/

 

 

/*负责释放scull设备数据区*/

int scull_trim(struct scull_dev *dev)

{

       struct scull_qset *next, *dptr;

       int qset,i;

       if(dev == NULL)     /*这里是我添加的判断指针不为空的语句*/

              return 0;

       qset = dev->qset;

       for (dptr = dev->data; dptr; dptr = next) { /*遍历所有的指针链表项 */

              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;    /*恢复scull_dev结构体中量子大小、数组大小等的默认值*/

       dev->quantum = scull_quantum;

       dev->qset = scull_qset;

       dev->data = NULL;

       return 0;

}

 

 

/*

 * Open and close

 */

 

int scull_open(struct inode *inode, struct file *filp)

{

       struct scull_dev *dev; /* device information */

 

       dev = container_of(inode->i_cdev, struct scull_dev, cdev);    //使用宏container_of获得指向结构体scull_dev的指针

       filp->private_data = dev; /* for other methods */

 

       //文件访问模式为只写

       if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) {

              if (down_interruptible(&dev->sem))    //等待信号量被中断,则返回ERESTARTSYS

                     return -ERESTARTSYS;

              scull_trim(dev); /* ignore errors */

              up(&dev->sem);    //释放信号量

       }

       return 0;          /* success */

}

 

int scull_release(struct inode *inode, struct file *filp)

{

       return 0;

}

 

/*n为所要遍历的链表项序号,如果该序号对应的链表为空则分配一个链表项,否则返回该链表项指针*/

struct scull_qset *scull_follow(struct scull_dev *dev, int n)

{

       struct scull_qset *qs = dev->data;

 

        /* Allocate first qset explicitly if need be */

       if (! qs) {

              qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);

              if (qs == NULL)

                     return NULL;  /* Never mind */

              memset(qs, 0, sizeof(struct scull_qset));

       }

 

       /* Then follow the list */

       while (n--) {

              if (!qs->next) {

                     qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);

                     if (qs->next == NULL)

                            return NULL;  /* Never mind */

                     memset(qs->next, 0, sizeof(struct scull_qset));

              }

              qs = qs->next;

              continue;

       }

       return qs;

}

 

/*

 * Data management: read and write

 */

 

ssize_t scull_read(struct file *filp, char __user *buf, size_t count,

                loff_t *f_pos)

{

       struct scull_dev *dev = filp->private_data;    //获取字符设备结构指针

       struct scull_qset *dptr;  /* 第一个链表项*/

       int quantum = dev->quantum, qset = dev->qset;

       int itemsize = quantum * qset; /*一个量子集中的字节数 */

       int item, s_pos, q_pos, rest;

       ssize_t retval = 0;

 

       if (down_interruptible(&dev->sem))    //等待信号量

              return -ERESTARTSYS;

       if (*f_pos >= dev->size)    //读取位置超出了文件中数据总量

              goto out;

       if (*f_pos + count > dev->size)  //读取位置后的数据少于read多要读取的字节数

              count = dev->size - *f_pos;

 

       /* 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 (defined elsewhere) */

       dptr = scull_follow(dev, item);    //根据链表项序数和dev指针找到对应读取的链表项指针

 

       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;

}

 

//scull_write类似scull_read

ssize_t scull_write(struct file *filp, const char __user *buf, size_t count,

                loff_t *f_pos)

{

       struct scull_dev *dev = filp->private_data;

       struct scull_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 = scull_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;

}

 

/*ioctl操作函数 */

int scull_ioctl(struct inode *inode, struct file *filp,

                 unsigned int cmd, unsigned long arg)

{

 

       int err = 0, tmp;

       int retval = 0;

   

       //检查ioctl命令的幻数是否是scull分配的字段,并检查命令是否在scull规定的范围内

       if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;

       if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;

 

       //使用access_ok检查用户空间地址的的合法性,若成功则返回1,否则返回0

       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) {

      //复位scull_quantumscull_qset为默认值

         case SCULL_IOCRESET:

              scull_quantum = SCULL_QUANTUM;

              scull_qset = SCULL_QSET;

              break;

        //通过指针设置scull_quantum的值

         case SCULL_IOCSQUANTUM: /* Set: arg points to the value */

              if (! capable (CAP_SYS_ADMIN)) //capable函数检查用户的特权级别

                     return -EPERM;

              retval = __get_user(scull_quantum, (int __user *)arg);  //从用户空间读取一个值到scull_quantum

              break;

      //通过传递值来设置scull_quantum的值

         case SCULL_IOCTQUANTUM: /* Tell: arg is the value */

              if (! capable (CAP_SYS_ADMIN))  //capable函数检查用户的特权级别

                     return -EPERM;

              scull_quantum = arg;  //这里直接将传递过来的值复制给scull_quantum

              break;

      //通过指针获取scull_quantum的值

         case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */

              retval = __put_user(scull_quantum, (int __user *)arg);

              break;

      //通过值获取scull_quantum的值

         case SCULL_IOCQQUANTUM: /* Query: return it (it's positive) */

              return scull_quantum;

      //通过指针交换

         case SCULL_IOCXQUANTUM: /* eXchange: use arg as pointer */

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              tmp = scull_quantum;

              retval = __get_user(scull_quantum, (int __user *)arg);

              if (retval == 0)

                     retval = __put_user(tmp, (int __user *)arg);

              break;

      //通过值交换

         case SCULL_IOCHQUANTUM: /* sHift: like Tell + Query */

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              tmp = scull_quantum;

              scull_quantum = arg;

              return tmp;

        /* scull_qset 的设置同上*/

         case SCULL_IOCSQSET:

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              retval = __get_user(scull_qset, (int __user *)arg);

              break;

 

         case SCULL_IOCTQSET:

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              scull_qset = arg;

              break;

 

         case SCULL_IOCGQSET:

              retval = __put_user(scull_qset, (int __user *)arg);

              break;

 

         case SCULL_IOCQQSET:

              return scull_qset;

 

         case SCULL_IOCXQSET:

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              tmp = scull_qset;

              retval = __get_user(scull_qset, (int __user *)arg);

              if (retval == 0)

                     retval = put_user(tmp, (int __user *)arg);

              break;

 

         case SCULL_IOCHQSET:

              if (! capable (CAP_SYS_ADMIN))

                     return -EPERM;

              tmp = scull_qset;

              scull_qset = arg;

              return tmp;

 

        /*

         * The following two change the buffer size for scullpipe.

         * The scullpipe device uses this same ioctl method, just to

         * write less code. Actually, it's the same driver, isn't it?

         */

 

         case SCULL_P_IOCTSIZE:

              scull_p_buffer = arg;

              break;

 

         case SCULL_P_IOCQSIZE:

              return scull_p_buffer;

 

 

         default:  /* redundant, as cmd was checked against MAXNR */

              return -ENOTTY;

       }

       return retval;

 

}

 

 

 

/*

 * The "extended" operations -- only seek

 */

 

loff_t scull_llseek(struct file *filp, loff_t off, int whence)

{

       struct scull_dev *dev = filp->private_data;

       loff_t newpos;

 

       switch(whence) {

         case 0: /* SEEK_SET */

              newpos = off;

              break;

 

         case 1: /* SEEK_CUR */

              newpos = filp->f_pos + off;

              break;

 

         case 2: /* SEEK_END */

              newpos = dev->size + off;

              break;

 

         default: /* can't happen */

              return -EINVAL;

       }

       if (newpos < 0) return -EINVAL;

       filp->f_pos = newpos;

       return newpos;

}

 

/*scull定义的文件操作结构体*/

struct file_operations scull_fops = {

       .owner =    THIS_MODULE,

       .llseek =   scull_llseek,

       .read =     scull_read,

       .write =    scull_write,

       .ioctl =    scull_ioctl,

       .open =     scull_open,

       .release =  scull_release,

};

 

/*

 * Finally, the module stuff

 */

 

/*

 * The cleanup function is used to handle initialization failures as well.

 * Thefore, it must be careful to work correctly even if some of the items

 * have not been initialized

 */

void scull_cleanup_module(void)

{

       int i;

       dev_t devno = MKDEV(scull_major, scull_minor);

 

       /* 在这里释放设备内存时,参考LDD3 P65的图3-1 scull设备的布局*/

       if (scull_devices) {

              for (i = 0; i < scull_nr_devs; i++) {    // scull_nr_devs为设备的数量

                     scull_trim(scull_devices + i);    //释放scull设备内存

                     cdev_del(&scull_devices[i].cdev);    //注销字符设备

              }

              kfree(scull_devices);    //释放scull设备

       }

 

#ifdef SCULL_DEBUG /* use proc only if debugging */

       scull_remove_proc();

#endif

 

       /* cleanup_module is never called if registering failed */

       unregister_chrdev_region(devno, scull_nr_devs);  //释放设备编号

 

       /* and call the cleanup functions for friend devices */

       scull_p_cleanup();

       scull_access_cleanup();

 

}

 

 

/*

 * Set up the char_dev structure for this device.

 */

static void scull_setup_cdev(struct scull_dev *dev, int index)

{

       int err, devno = MKDEV(scull_major, scull_minor + index);

    

       cdev_init(&dev->cdev, &scull_fops);

       dev->cdev.owner = THIS_MODULE;

       dev->cdev.ops = &scull_fops;

       err = cdev_add (&dev->cdev, devno, 1);

       /* Fail gracefully if need be */

       if (err)

              printk(KERN_NOTICE "Error %d adding scull%d", err, index);

}

 

 

int scull_init_module(void)

{

       int result, i;

       dev_t dev = 0;

 

/*

 * Get a range of minor numbers to work with, asking for a dynamic

 * major unless directed otherwise at load time.

 */

       if (scull_major) {    //由于这里的scull_major默认为0,将会进行动态分配设备号

              dev = MKDEV(scull_major, scull_minor);  //获得设备号

              result = register_chrdev_region(dev, scull_nr_devs, "scull");  //注册设备号

       } else {

              result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,

                            "scull");    //动态分配设备号

              scull_major = MAJOR(dev);

       }

       if (result < 0) {

              printk(KERN_WARNING "scull: can't get major %d\n", scull_major);

              return result;

       }

 

    //分配scull设备结构体

       scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), GFP_KERNEL);

       if (!scull_devices) {

              result = -ENOMEM;

              goto fail;  /* Make this more graceful */

       }

       memset(scull_devices, 0, scull_nr_devs * sizeof(struct scull_dev));

 

    //根据量子大小等变量来初始化scull设备结构体

       for (i = 0; i < scull_nr_devs; i++) {

              scull_devices[i].quantum = scull_quantum;

              scull_devices[i].qset = scull_qset;

              init_MUTEX(&scull_devices[i].sem);  //初始化信号量

              scull_setup_cdev(&scull_devices[i], i);  //该函数封装了字符设备注册函数

       }

 

        /* At this point call the init function for any friend device */

       dev = MKDEV(scull_major, scull_minor + scull_nr_devs);

       dev += scull_p_init(dev);

       dev += scull_access_init(dev);

 

#ifdef SCULL_DEBUG /* only when debugging */

       scull_create_proc();

#endif

 

       return 0; /* succeed */

 

  fail:    //初始化模块出错处理

       scull_cleanup_module();

       return result;

}

 

module_init(scull_init_module);

module_exit(scull_cleanup_module);

 

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