scull源码分析 //未完待续,囧

scull源码分析 //未完待续,囧

Makefile

 

#  disable/enable debugging
#
DEBUG = y

#  当DEBUG变量等于y时。两个比较变量用括号括起来,逗号分隔。ifeq和括号中间有一个空格。
ifeq ($(DEBUG), y)  
#  += 追加变量值。如果该变量之前没有被定义过,+=就自动变成=,变量被定义成递归展开式的变量;如果之前已经定义过,就遵循之前的风格。
#
 = 递归展开式变量:在定义时,变量中对其它变量的引用不会被替换展开。变量在引用它的地方被替换展开时,变量中其它变量才被同时替换展开。
#
 -O 程序优化参数;
#
 -g 使生成的debuginfo包额外支持gnu和gdb调试程序,易于使用
#
 -DSCULL_DEBUG 即define SCULL_DEBUG   TODO
    DEBFLAGS  +=   - - - DSCULL_DEBUG  #  -O is needed to expand inlines
else
    DEBFLAGS 
+=   - O2
endif

#   CFLAGS影响编译过程。应在遵循原设置的基础上添加新的设置,注意+=
CFLAGS  +=  $(DEBFLAGS)
#  -I 选项指出头文件位置。LDDINC是下面定义的一个变量。
CFLAGS  +=   - I$(LDDINC)

#  如果KERNELRELEASE不等于空。ifneq判断参数是否不相等。
ifneq ($(KERNELRELEASE),)
#  := 直接展开式变量:在定义时就展开变量中对其它变量或函数的引用,定以后就成了需要定义的文本串。不能实现对其后定义变量的引用。
scull - objs : =  main.o pipe.o access.o
obj
- m : =  scull.o
#  否则(KERNELRELEASE是空)
else
#  ?= 条件赋值:只有在此变量之前没有赋值的情况下才会被赋值。
#
 shell uname r 内核版本号
KERNELDIR ? =   / lib / modules / $(shell uname r) / build
#  shell pwd 当前在文件系统中的路径
PWD : =  $(shell pwd)
 
modules:
    
#  $(MAKE) TODO
     #  -C $(KERNELDIR) 在读取Makefile之前进入$(KERNELDIR)目录
     #  M=$(PWD) LDDINC=$(PWD)/../include 传递2个变量给Makefile
     #  modules 是$(KERNELDIR)中的Makefile的target   TODO
    $(MAKE)  - C $(KERNELDIR) M = $(PWD) LDDINC = $(PWD) / .. / include modules

endif

#  清理
clean:
    rm 
- rf  * .o  *~  core .depend . * .cmd  * .ko  * .mod.c .tmp_versions

#  TODO
#
 产生依赖信息文件,如果存在的话则将其包含到Makefile中。
depend .depend dep:
    $(CC) $(CFLAGS) 
- * .c  >  .depend
    
ifeq (.depend,$(wildcard .depend))
include .depend
endif

---
参考
1.[GNU make中文手册]
2.[CFLAGS 统一和 gcc 3.4] http://www.magiclinux.org/node/821
3.[LDD3源码学习笔记之scull_main] http://www.sudu.cn/info/html/edu/20070101/291462.html
4.http://topic.csdn.net/u/20070815/22/cbd2f64d-f6e3-4938-97f8-4f8fe5a21465.html

 

 scull.h

 

/*
 * scull.h -- definitions for the 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.
 *
 * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $
 
*/

#ifndef _SCULL_H_
#define  _SCULL_H_

#include 
< linux / ioctl.h >   /*  needed for the _IOW etc stuff used later  */

/*
 * Macros to help debugging
 
*/

//  undef取消以前定义的宏
#undef  PDEBUG             /* undef it, just in case */
#ifdef SCULL_DEBUG
#  ifdef __KERNEL__
     
//  内核空间,printk 输出调试信息
      /*  This one if debugging is on, and kernel space  */
#    define PDEBUG(fmt, args) printk( KERN_DEBUG 
" scull:  "  fmt, ## args)
#  
else
     
//  用户空间,fprintf 输出调试信息
      /*  This one for user space  */
#    define PDEBUG(fmt, args) fprintf(stderr, fmt, ## args)
#  endif
#else
#  define PDEBUG(fmt, args
/*  not debugging: nothing  */
#endif

#undef  PDEBUGG
#define  PDEBUGG(fmt, args) /* nothing: it's a placeholder */

#ifndef SCULL_MAJOR
//  主设备号指定为0,表示由内核动态分配
#define  SCULL_MAJOR 0   /* dynamic major by default */
#endif

//  bare device的数量,也就是要请求的连续设备编号的总数
#ifndef SCULL_NR_DEVS
#define  SCULL_NR_DEVS 4    /* scull0 through scull3 */
#endif

//  TODO
#ifndef SCULL_P_NR_DEVS
#define  SCULL_P_NR_DEVS 4  /* scullpipe0 through scullpipe3 */
#endif

/*
 * The bare device is a variable-length region of memory.
 * Use a linked list of indirect blocks.
 *
 * "scull_dev->data" points to an array of pointers, each
 * pointer refers to a memory area of SCULL_QUANTUM bytes.
 *
 * The array (quantum-set) is SCULL_QSET long.
 
*/
 
//  每个内存区字节数。(量子长度)
#ifndef SCULL_QUANTUM
#define  SCULL_QUANTUM 4000
#endif

//  数组长度(量子集长度)
#ifndef SCULL_QSET
#define  SCULL_QSET    1000
#endif

/*
 * The pipe device is a simple circular buffer. Here its default size
 
*/
//  管道设备,环形缓冲大小。 
#ifndef SCULL_P_BUFFER
#define  SCULL_P_BUFFER 4000
#endif

/*
 * Representation of scull quantum sets.
 
*/
struct  scull_qset {
    
void   ** data;
    
struct  scull_qset  * next;
};

//  使用scull_dev表示每个设备
struct  scull_dev {
    
struct  scull_qset  * data;   /*  Pointer to first quantum set  */
    
int  quantum;               /*  the current quantum size  */
    
int  qset;                  /*  the current array size  */
    unsigned 
long  size;        /*  amount of data stored here  */
    unsigned 
int  access_key;   /*  used by sculluid and scullpriv  */
    
struct  semaphore sem;      /*  mutual exclusion semaphore      */
    
struct  cdev cdev;       /*  Char device structure         */
};

//  TODO 什么用?
/*

 * Split minors in two parts
 
*/
//  高四位
#define  TYPE(minor)    (((minor) >> 4) & 0xf)    /* high nibble */
//  低四位
#define  NUM(minor)    ((minor) & 0xf)        /* low  nibble */


/*
 * The different configurable parameters
 
*/
extern   int  scull_major;      /*  main.c  */
extern   int  scull_nr_devs;
extern   int  scull_quantum;
extern   int  scull_qset;

extern   int  scull_p_buffer;     /*  pipe.c  */


/*
 * Prototypes for shared functions
 
*/

int      scull_p_init(dev_t dev);
void     scull_p_cleanup( void );
int      scull_access_init(dev_t dev);
void     scull_access_cleanup( void );

int      scull_trim( struct  scull_dev  * dev);

ssize_t scull_read(
struct  file  * filp,  char  __user  * buf, size_t count,
                   loff_t 
* f_pos);
ssize_t scull_write(
struct  file  * filp,  const   char  __user  * buf, size_t count,
                    loff_t 
* f_pos);
loff_t  scull_llseek(
struct  file  * filp, loff_t off,  int  whence);
int      scull_ioctl( struct  inode  * inode,  struct  file  * filp,
                    unsigned 
int  cmd, unsigned  long  arg);


/*
 * Ioctl definitions
 
*/

//  TODO 魔数什么用?
/*
 Use 'k' as magic number  */
#define  SCULL_IOC_MAGIC  'k'
/*  Please use a different 8-bit number in your code  */

#define  SCULL_IOCRESET    _IO(SCULL_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
 * X means "eXchange": switch G and S atomically
 * H means "sHift": switch T and Q atomically
 
*/
#define  SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)
#define  SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)
#define  SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)
#define  SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)
#define  SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)
#define  SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)
#define  SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)
#define  SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)
#define  SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)
#define  SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)
#define  SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)
#define  SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)

/*
 * The other entities only have "Tell" and "Query", because they're
 * not printed in the book, and there's no need to have all six.
 * (The previous stuff was only there to show different ways to do it.
 
*/
#define  SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC,   13)
#define  SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC,   14)
/*   more to come  */

#define  SCULL_IOC_MAXNR 14

#endif  /* _SCULL_H_ */

 

main.c

 

/*
 * 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 
< linux / config.h >
#include 
< linux / module.h >
#include 
< linux / moduleparam.h >
#include 
< linux / init.h >

#include 
< linux / kernel.h >      /*  printk()  */
#include 
< linux / slab.h >          /*  kmalloc()  */
#include 
< linux / fs.h >          /*  everything  */
#include 
< linux / errno.h >      /*  error codes  */
#include 
< linux / types.h >      /*  size_t  */
#include 
< linux / proc_fs.h >
#include 
< linux / fcntl.h >      /*  O_ACCMODE  */
#include 
< linux / seq_file.h >
#include 
< linux / cdev.h >

#include 
< asm / system.h >          /*  cli(), *_flags  */
#include 
< asm / uaccess.h >      /*  copy_*_user  */

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

/*
 * Our parameters which can be set at load time.
 
*/

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;  //  量子集大小

//  插入模块时指定的参数
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;     /*  allocated in scull_init_module  */


//  释放整个数据区。简单遍历列表并且释放它发现的任何量子和量子集。
//  在scull_open 在文件为写而打开时调用。
//  调用这个函数时必须持有信号量。
/*

 * Empty out the scull device; must be called with the device
 * semaphore held.
 
*/
int  scull_trim( struct  scull_dev  * dev)
{
    
struct  scull_qset  * next,  * dptr;
    
int  qset  =  dev -> qset;    /*  "dev" is not-null  */   //  量子集大小
     int  i;

    
//  遍历多个量子集。dev->data 指向第一个量子集。
     for  (dptr  =  dev -> data; dptr; dptr  =  next) {  /*  all the list items  */
        
if  (dptr -> data) {  //  量子集中有数据
             for  (i  =   0 ; i  <  qset; i ++ //  遍历释放当前量子集中的每个量子。量子集大小为qset。 
                kfree(dptr -> data[i]);
            kfree(dptr
-> data);  //  释放量子指针数组
            dptr -> data  =  NULL; 
        }
        
//  next获取下一个量子集,释放当前量子集。
        next  =  dptr -> next;
        kfree(dptr);
    }
    
//  清理struct scull_dev dev中变量的值
    dev -> size  =   0 ;
    dev
-> quantum  =  scull_quantum;
    dev
-> qset  =  scull_qset;
    dev
-> data  =  NULL;
    
return   0 ;
}
#ifdef SCULL_DEBUG 
/*  use proc only if debugging  */
/*
 * The proc filesystem: function to read and entry
 
*/

 
//  在proc里实现文件,在文件被读时产生数据
 
//  当一个进程读 /proc 文件,内核分配了一页内存,驱动可以写入数据返回用户空间
int  scull_read_procmem( char   * buf,   //  写数据的缓冲区
                 char   ** start,   //  数据写在页中的哪里
                off_t offset, 
                
int  count, 
                
int   * eof,   //  必须被驱动设置,表示写数据结束
                 void   * data)  //  data传递私有数据
{
    
int  i, j, len  =   0 ;
    
int  limit  =  count  -   80 /*  Don't print more than this  */

    
//  遍历每个设备,输出总字节数小于limit
     for  (i  =   0 ; i  <  scull_nr_devs  &&  len  <=  limit; i ++ ) { 
        
struct  scull_dev  * =   & scull_devices[i];
        
struct  scull_qset  * qs  =  d -> data;
        
if  (down_interruptible( & d -> sem))
            
return   - ERESTARTSYS;
        len 
+=  sprintf(buf + len, " \nDevice %i: qset %i, q %i, sz %li\n " ,   //  输出当前是第几个scull设备,量子集大小,量子大小,设备大小
                i, d -> qset, d -> quantum, d -> size);
        
//  遍历当前设备的每个量子集
         for  (; qs  &&  len  <=  limit; qs  =  qs -> next) {  /*  scan the list  */  
            len 
+=  sprintf(buf  +  len,  "   item at %p, qset at %p\n " ,      //  输出当前量子集 和 量子集中的数据 在内存中的位置。
                    qs, qs -> data);
            
//  输出最后一个量子集中,每个量子的地址
             if  (qs -> data  &&   ! qs -> next)  /*  dump only the last item  */  
                
for  (j  =   0 ; j  <  d -> qset; j ++ ) {
                    
if  (qs -> data[j])
                        len 
+=  sprintf(buf  +  len,
                                
"     % 4i: %8p\n " ,
                                j, qs
-> data[j]);
                }
        }
        up(
& scull_devices[i].sem);
    }
    
* eof  =   1 ;
    
return  len;
}



///  seq_file接口:创建一个虚拟文件,遍历一串数据,这些数据必须返回用户空间。 
//  步骤:start, next, stop, show
/*

 * For now, the seq_file implementation will exist in parallel.  The
 * older read_procmem function should maybe go away, though.
 
*/

/*
 * Here are our sequence iteration methods.  Our "position" is
 * simply the device number.
 
*/
//  参数:*s总被忽略;pos指从哪儿开始读,具体意义依赖于实现。
//  seq_file典型的实现是遍历一感兴趣的数据序列,pos就用来指示序列中的下一个元素。
//  在scull中,pos简单地作为scull_array数组的索引。
static   void   * scull_seq_start( struct  seq_file  * s, loff_t  * pos)
{
    
if  ( * pos  >=  scull_nr_devs)
        
return  NULL;    /*  No more to read  */
    
return  scull_devices  +   * pos;  //  返回索引号是pos的scull设备
}

//  返回下一个scull设备
static   void   * scull_seq_next( struct  seq_file  * s,  void   * v, loff_t  * pos)
{
    (
* pos) ++ ;
    
if  ( * pos  >=  scull_nr_devs)
        
return  NULL;
    
return  scull_devices  +   * pos;
}

static   void  scull_seq_stop( struct  seq_file  * s,  void   * v)
{
    
/*  Actually, there's nothing to do here  */
}

//  输出迭代器v生成的数据到用户空间
static   int  scull_seq_show( struct  seq_file  * s,  void   * v)
{
    
struct  scull_dev  * dev  =  ( struct  scull_dev  * ) v;
    
struct  scull_qset  * d;
    
int  i;

    
if  (down_interruptible( & dev -> sem))
        
return   - ERESTARTSYS;
    seq_printf(s, 
" \nDevice %i: qset %i, q %i, sz %li\n " //  输出,相当于用于空间的printf。返回非0值表示缓冲区满,超出的数据被丢弃。
            ( int ) (dev  -  scull_devices), dev -> qset,
            dev
-> quantum, dev -> size);
     
//  遍历设备的每一个量子集
     for  (d  =  dev -> data; d; d  =  d -> next) {  /*  scan the list  */
        seq_printf(s, 
"   item at %p, qset at %p\n " , d, d -> data);  //  量子集地址,量子集中数据地址
        
//  输出最后一个量子集中,每个量子的地址
         if  (d -> data  &&   ! d -> next)  /*  dump only the last item  */
            
for  (i  =   0 ; i  <  dev -> qset; i ++ ) {
                
if  (d -> data[i])
                    seq_printf(s, 
"     % 4i: %8p\n " ,
                            i, d
-> data[i]);
            }
    }
    up(
& dev -> sem);
    
return   0 ;
}

//  seq_file的操作集
/*

 * Tie the sequence operators up.
 
*/
static   struct  seq_operations scull_seq_ops  =  {
    .start 
=  scull_seq_start,
    .next  
=  scull_seq_next,
    .stop  
=  scull_seq_stop,
    .show  
=  scull_seq_show
};

//  打开 /proc 文件。在这里也就是初始化seq_file
/*

 * Now to implement the /proc file we need only make an open
 * method which sets up the sequence operators.
 
*/
static   int  scull_proc_open( struct  inode  * inode,  struct  file  * file)
{
    
return  seq_open(file,  & scull_seq_ops);  //  连接file和seq_file
}

//  proc文件的操作集
/*

 * Create a set of file operations for our proc file.
 
*/
static   struct  file_operations scull_proc_ops  =  {
    .owner   
=  THIS_MODULE,
    .open    
=  scull_proc_open,
    .read    
=  seq_read,
    .llseek  
=  seq_lseek,
    .release 
=  seq_release
};
    

 
/*
 * Actually create (and remove) the /proc file(s).
 
*/
//  建立通过proc方式debug时需要的proc文件
static   void  scull_create_proc( void )
{
    
struct  proc_dir_entry  * entry;

    
// TODO 创建了两个文件?
    
//  创建文件scullmem并使之关联read函数
    create_proc_read_entry( " scullmem " ,    //  name:要创建的文件名 
             0   /*  default mode  */ ,    //  mode:文件掩码,为0则按照系统默认的掩码创建文件
            NULL  /*  parent dir  */ ,   //  base:指定文件所在目录,为NULL则被创建在/proc根目录下
            scull_read_procmem,   //  read_proc:处理读请求的回调函数
            NULL  /*  client data  */ );  //  内核忽略此参数,但会把它当做参数传递给read_proc

    
    entry 
=  create_proc_entry( " scullseq " 0 , NULL);  //  参数:名字,掩码,父目录
                                                   
//  create_proc_entry 同样用来建立/proc文件,但较create_proc_read_entry 更为底层一些
     if  (entry)
        entry
-> proc_fops  =   & scull_proc_ops;
}

//  移除创建的 /proc 文件
static   void  scull_remove_proc( void )
{
    
/*  no problem if it was not registered  */
    
//  移除一个proc_dir_entry, 如果这个结构还在使用,设置deleted标志,返回
    remove_proc_entry( " scullmem " ,    //  const char *name
                    NULL  /*  parent dir  */ );     //  struct proc_dir_entry *parent
    remove_proc_entry( " scullseq " , NULL);
}


#endif  /* SCULL_DEBUG */





/*
 * Open and close
 
*/
///  打开设备:文件私有数据,设置成对应的scull_dev
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);
    filp
-> private_data  =  dev;  /*  for other methods  */

    
/*  now trim to 0 the length of the device if open was write-only  */
    
///  文件以只读模式打开时,截断为0
     if  ( (filp -> f_flags  &  O_ACCMODE)  ==  O_WRONLY) {
        
if  (down_interruptible( & dev -> sem))
            
return   - ERESTARTSYS;
        scull_trim(dev); 
/*  ignore errors  */
        up(
& dev -> sem);
    }
    
return   0 ;           /*  success  */
}

//  file_operations 中的.release
int  scull_release( struct  inode  * inode,  struct  file  * filp)
{
    
return   0 ;
}
/*
 * Follow the list
 
*/
 
//  返回设备dev的第n个量子集,量子集不够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) {
                             
//  kmalloc 内核模块中,动态分配连续的物理地址,用于小内存分配
        qs  =  dev -> data  =  kmalloc( sizeof ( struct  scull_qset),  //  size_t size. 要分配的块的大小
                                GFP_KERNEL);     //  int flags. GFP_KERNEL 在当前进程缺少内存时,可以睡眠来等待一页。 TODO
                                                
//  使用 GFP_KERNEL 来分配内存的函数,必须可重入 且不能在原子上下文中运行。
         if  (qs  ==  NULL)
            
return  NULL;   /*  Never mind  */
        memset(qs, 
0 sizeof ( struct  scull_qset));
    }

    
/*  Then follow the list  */
    
//  遍历当前设备的量子集链表n步,量子集不够就申请新的。
     while  (n -- ) {
        
if  ( ! qs -> next) {
            qs
-> next  =  kmalloc( sizeof ( struct  scull_qset), GFP_KERNEL);
            
if  (qs -> next  ==  NULL)  //  不关心内存分配失败? TODO
                 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)           //  要读的位置,在filp私有数据中的偏移
{
    
struct  scull_dev  * dev  =  filp -> private_data; 
    
struct  scull_qset  * dptr;     /*  the first listitem  */
    
int  quantum  =  dev -> quantum, qset  =  dev -> qset;  //  量子、量子集大小
     int  itemsize  =  quantum  *  qset;  /*  how many bytes in the listitem  */   //  一个量子集的字节数
     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 ;
    
//  要读的count超出了size,裁断count
     if  ( * f_pos  +  count  >  dev -> size)
        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);

    
if  (dptr  ==  NULL  ||   ! dptr -> data  ||   !  dptr -> data[s_pos])  //  没有量子集,量子集中没有data,没有第s_pos个量子
         goto   out /*  don't fill holes  */

    
/*  read only up to the end of this quantum  */
    
//  只在一个量子中读:如果count超出当前量子,截断count
     if  (count  >  quantum  -  q_pos)
        count 
=  quantum  -  q_pos;

    
//  将读位置的内容复制到用户空间buf,共复制count字节
     if  (copy_to_user(buf,        //  void __user *to
                    dptr -> data[s_pos]  +  q_pos,   //  const void *from
                    count)) {    //  unsigned long n
        retval  =   - EFAULT;
        
goto   out ;
    }
    
* f_pos  +=  count;
    retval 
=  count;

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

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);      //  获取设备dev的第item个量子集
     if  (dptr  ==  NULL)
        
goto   out ;
    
//  如果该量子集数据是NULL,就申请一块新内存
     if  ( ! dptr -> data) {
        dptr
-> data  =  kmalloc(qset  *   sizeof ( char   * ), GFP_KERNEL);
        
if  ( ! dptr -> data)
            
goto   out ;
        memset(dptr
-> data,  0 , qset  *   sizeof ( char   * ));
    }
    
//  如果第s_pos个量子是NULL,申请一块新内存
     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)) {  //  从用户空间拷贝数据到内核空间,失败返回没有拷贝的字节数,成功返回0
        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;
}

/*
 * The ioctl() implementation
 
*/

int  scull_ioctl( struct  inode  * inode, 
                
struct  file  * filp,       //  设备文件
                unsigned  int  cmd,        //  功能号
                unsigned  long  arg)       //  参数: 值,或者用户空间指针
{

    
int  err  =   0 , tmp;
    
int  retval  =   0 ;
    
    
/*
     * extract the type and number bitfields, and don't decode
     * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
     
*/
    
//  对错误的命令,返回ENOTTY    TODO
     if  (_IOC_TYPE(cmd)  !=  SCULL_IOC_MAGIC)  return   - ENOTTY;    //  提取ioctl的类型和功能号,不解码
     if  (_IOC_NR(cmd)  >  SCULL_IOC_MAXNR)  return   - ENOTTY;

    
/*
     * the direction is a bitmask, and VERIFY_WRITE catches R/W
     * transfers. `Type' is user-oriented, while
     * access_ok is kernel-oriented, so the concept of "read" and
     * "write" is reversed
     
*/
    
//  如果该ioctl为了读数据,检查当前进程是否可写arg(写到用户空间,用户就读到数据了);如果为了写数据,检查arg是否可读
     if  (_IOC_DIR(cmd)  &  _IOC_READ)   //  _IOC_DIR 获取读写属性域值
        
//  access_ok() 如果当前进程被允许访问用户空间addr处的内存,返回1,否则返回0
        err  =   ! access_ok(VERIFY_WRITE,   //  type: Type of access: %VERIFY_READ or %VERIFY_WRITE. 
                        ( void  __user  * )arg,   //  addr: User space pointer to start of block to check
                        _IOC_SIZE(cmd));      //  size: Size of block to check. _IOC_SIZE() 读取数据大小域值
     else   if  (_IOC_DIR(cmd)  &  _IOC_WRITE)
        err 
=    ! access_ok(VERIFY_READ, ( void  __user  * )arg, _IOC_SIZE(cmd));
    
if  (err)  return   - EFAULT;

    
switch (cmd) {

      
//  重置量子集、量子大小 TODO:相关内存不同时整理?
       case  SCULL_IOCRESET:
        scull_quantum 
=  SCULL_QUANTUM;
        scull_qset 
=  SCULL_QSET;
        
break ;

      
//  设置量子大小,arg是指向量子大小值的指针        
       case  SCULL_IOCSQUANTUM:  /*  Set: arg points to the value  */
        
if  ( !  capable (CAP_SYS_ADMIN))  //  检查是否包含系统管理权限
             return   - EPERM;
        
//  取arg所指内容,赋值给scull_quantum
        
//  __get_user() 从用户空间获取一个简单变量,基本不做检查
        retval  =  __get_user(scull_quantum,  //  x: variable to store result.
                            ( int  __user  * )arg);  //  ptr: source address, in user space
         break ;

      
//  设置量子大小, arg是值
       case  SCULL_IOCTQUANTUM:  /*  Tell: arg is the value  */
        
if  ( !  capable (CAP_SYS_ADMIN))
            
return   - EPERM;
        scull_quantum 
=  arg;
        
break ;

      
//  获取量子大小,arg是指针
       case  SCULL_IOCGQUANTUM:  /*  Get: arg is pointer to result  */
        retval 
=  __put_user(scull_quantum, ( int  __user  * )arg);
        
break ;

      
//  查询量子大小,返回值 TODO 怎么赋值
       case  SCULL_IOCQQUANTUM:  /*  Query: return it (it's positive)  */
        
return  scull_quantum;

      
//  交换量子大小,指针:按arg指向值设置量子大小,当前量子大小保存到arg指向空间
       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;

      
//  量子集大小,和上面量子功能类似
       case  SCULL_IOCSQSET:   //  set
         if  ( !  capable (CAP_SYS_ADMIN))
            
return   - EPERM;
        retval 
=  __get_user(scull_qset, ( int  __user  * )arg);
        
break ;

      
case  SCULL_IOCTQSET:  //  tell
         if  ( !  capable (CAP_SYS_ADMIN))
            
return   - EPERM;
        scull_qset 
=  arg;
        
break ;

      
case  SCULL_IOCGQSET:  //  get
        retval  =  __put_user(scull_qset, ( int  __user  * )arg);
        
break ;

      
case  SCULL_IOCQQSET:  //  query
         return  scull_qset;

      
case  SCULL_IOCXQSET:  //  eXchange
         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:  //  sHift
         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?
         
*/

      
//  tell & query 管道设备缓存大小
       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)          //  seek方式
{
    
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;
}



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);  //  MKDEV 把主次设备号合成为一个dev_t结构

    
/*  Get rid of our char dev entries  */
    
//  清除字符设备入口
     if  (scull_devices) {
        
//  遍历释放每个设备的数据区
         for  (i  =   0 ; i  <  scull_nr_devs; i ++ ) {
            scull_trim(scull_devices 
+  i);   //  释放数据区
            cdev_del( & scull_devices[i].cdev);  //  移除cdev
        }
        kfree(scull_devices);  
//  释放scull_devices本身
    }

//  如果使用了/proc来debug,移除创建的/proc文件
#ifdef SCULL_DEBUG  /*  use proc only if debugging  */
    scull_remove_proc();
#endif

    
/*  cleanup_module is never called if registering failed  */
    
//  解注册scull_nr_devs个设备号,从devno开始
    unregister_chrdev_region(devno,          //  dev_t from 
                             scull_nr_devs);  //  unsigned count

    
/*  and call the cleanup functions for friend devices  */
    scull_p_cleanup();
    scull_access_cleanup();

}


/*
 * Set up the char_dev structure for this device.
 
*/
 
//  建立 char_dev 结构
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;
    
//  添加字符设备dev->cdev,立即生效
    err  =  cdev_add ( & dev -> cdev,      //  struct cdev *p: the cdev structure for the device
                    devno,           //  dev_t dev: 第一个设备号
                     1 );              //  unsigned count: 该设备连续次设备号的数目
     /*  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) {
        dev 
=  MKDEV(scull_major, scull_minor);
        
//  register_chrdev_region 用于已知起始设备的设备号的情况
        result  =  register_chrdev_region(dev, scull_nr_devs,  " scull " );
    } 
else  {
        
//  alloc_chrdev_region 用于设备号未知,向系统动态申请未被占用的设备号情况
        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;
    }

        
/*  
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     
*/
    
//  给scull_dev对象申请内存
    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_dev
         /*  Initialize each device.  */
    
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);  //  初始化互斥锁,把信号量sem置为1
        scull_setup_cdev( & scull_devices[i], i);  //  建立char_dev结构
    }

        
/*  At this point call the init function for any friend device  */
    
//  TODO
    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);


access.c
/*
 * access.c -- the files with access control on open
 *
 * 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.
 *
 * $Id: access.c,v 1.17 2004/09/26 07:29:56 gregkh Exp $
 
*/

/*  FIXME: cloned devices as a use for kobjects?  */
 
#include 
< linux / kernel.h >   /*  printk()  */
#include 
< linux / module.h >
#include 
< linux / slab.h >     /*  kmalloc()  */
#include 
< linux / fs.h >       /*  everything  */
#include 
< linux / errno.h >    /*  error codes  */
#include 
< linux / types.h >    /*  size_t  */
#include 
< linux / fcntl.h >
#include 
< linux / cdev.h >
#include 
< linux / tty.h >
#include 
< asm / atomic.h >
#include 
< linux / list.h >

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

static  dev_t scull_a_firstdev;   /*  Where our range begins  */

/*
 * These devices fall back on the main scull operations. They only
 * differ in the implementation of open() and close()
 
*/

//  这些设备主要的scull操作是相同的,仅读写实现不同?



/* ***********************************************************************
 *
 * The first device is the single-open one,
 *  it has an hw structure and an open count
 
*/
//  第一个设备,只能打开一次。有一个hw结构,和打开计数。 TODO: hw结构是什么?

static   struct  scull_dev scull_s_device;
//  原子类型,用于计数。初始化原子值为1.
static  atomic_t scull_s_available  =  ATOMIC_INIT( 1 ); 

static   int  scull_s_open( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_dev  * dev  =   & scull_s_device;  /*  device information  */

    
//  如果scull_s_available计数减1不等于0,计数加1,返回-EBUSY
      if  ( !  atomic_dec_and_test ( & scull_s_available)) {  //  递减原子值;检查是0返回true,否则false
         atomic_inc( & scull_s_available);
        
return   - EBUSY;  /*  already open  */
    }

    
//  否则打开设备
     /*  then, everything else is copied from the bare scull device  */
    
if  ( (filp -> f_flags  &  O_ACCMODE)  ==  O_WRONLY)
        scull_trim(dev);
    filp
-> private_data  =  dev;
    
return   0 ;           /*  success  */
}

static   int  scull_s_release( struct  inode  * inode,  struct  file  * filp)
{
    
//  scull_s_available加1,恢复1了
    atomic_inc( & scull_s_available);  /*  release the device  */
    
return   0 ;
}


/*
 * The other operations for the single-open device come from the bare device
 
*/
struct  file_operations scull_sngl_fops  =  {
    .owner 
=     THIS_MODULE,
    .llseek 
=          scull_llseek,
    .read 
=            scull_read,
    .write 
=           scull_write,
    .ioctl 
=           scull_ioctl,
    .open 
=            scull_s_open,
    .release 
=         scull_s_release,
};


/* ***********************************************************************
 *
 * Next, the "uid" device. It can be opened multiple times by the
 * same user, but access is denied to other users if the device is open
 
*/

//  "uid"设备。可以被同一用户多次打开。拒绝其他用户同时打开。

static   struct  scull_dev scull_u_device;
static   int  scull_u_count;     /*  initialized to 0 by default  */
static  uid_t scull_u_owner;     /*  initialized to 0 by default  */
static  spinlock_t scull_u_lock  =  SPIN_LOCK_UNLOCKED;  //  初始化自旋锁为0

static   int  scull_u_open( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_dev  * dev  =   & scull_u_device;  /*  device information  */

    
//  获取自旋锁
    spin_lock( & scull_u_lock);
    
//  如果设备已经被打开,且已打开设备的用户不是当前进程的uid或euid,且不具备CAP_DAC_OVERRIDE能力,则释放自旋锁,返回-EBUSY
    
//  uid表示进程的创建者,euid表示进程对文件和资源的访问权限(具备等同于哪个用户的权限)
    
//  CAP_DAC_OVERRIDE 越过在文件和目录上的访问限制的能力
     if  (scull_u_count  &&  
            (scull_u_owner 
!=  current -> uid)  &&    /*  allow user  */
            (scull_u_owner 
!=  current -> euid)  &&   /*  allow whoever did su  */
            
! capable(CAP_DAC_OVERRIDE)) {  /*  still allow root  */
        spin_unlock(
& scull_u_lock);
        
return   - EBUSY;    /*  -EPERM would confuse the user  */
    }

    
//  如果是第一次打开设备,scull_u_owner设置成当前进程uid
     if  (scull_u_count  ==   0 )
        scull_u_owner 
=  current -> uid;  /*  grab it  */

    
//  递增打开计数。释放自旋锁。
    scull_u_count ++ ;
    spin_unlock(
& scull_u_lock);

/*  then, everything else is copied from the bare scull device  */
    
//  打开设备

    
if  ((filp -> f_flags  &  O_ACCMODE)  ==  O_WRONLY)
        scull_trim(dev);
    filp
-> private_data  =  dev;
    
return   0 ;           /*  success  */
}

static   int  scull_u_release( struct  inode  * inode,  struct  file  * filp)
{
    
//  获取自旋锁,递减打开计数,释放自旋锁。
    spin_lock( & scull_u_lock);
    scull_u_count
-- /*  nothing else  */
    spin_unlock(
& scull_u_lock);
    
return   0 ;
}



/*
 * The other operations for the device come from the bare device
 
*/
struct  file_operations scull_user_fops  =  {
    .owner 
=       THIS_MODULE,
    .llseek 
=      scull_llseek,
    .read 
=        scull_read,
    .write 
=       scull_write,
    .ioctl 
=       scull_ioctl,
    .open 
=        scull_u_open,
    .release 
=     scull_u_release,
};

/* ***********************************************************************
 *
 * Next, the device with blocking-open based on uid
 
*/
//  设备阻塞于open TODO

static   struct  scull_dev scull_w_device;
static   int  scull_w_count;     /*  initialized to 0 by default  */
static  uid_t scull_w_owner;     /*  initialized to 0 by default  */
static  DECLARE_WAIT_QUEUE_HEAD(scull_w_wait);
static  spinlock_t scull_w_lock  =  SPIN_LOCK_UNLOCKED;

//  判断设备是否可用
static  inline  int  scull_w_available( void )
{
    
//  设备没有被打开,或已打开设备的用户是当前进程的uid或euid,或具备CAP_DAC_OVERRIDE能力,则可用
     return  scull_w_count  ==   0   ||
        scull_w_owner 
==  current -> uid  ||
        scull_w_owner 
==  current -> euid  ||
        capable(CAP_DAC_OVERRIDE);
}


static   int  scull_w_open( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_dev  * dev  =   & scull_w_device;  /*  device information  */

    
//  获取自旋锁
    spin_lock( & scull_w_lock);

    
//  如果设备不可用,则释放自旋锁,等待设备可用后重新申请自旋锁,循环检查设备是否可用
     while  ( !  scull_w_available()) {
        spin_unlock(
& scull_w_lock);
        
if  (filp -> f_flags  &  O_NONBLOCK)  return   - EAGAIN;  //  如果文件以非阻塞模式打开,返回-EAGAIN,让用户层程序再试
         if  (wait_event_interruptible (scull_w_wait, scull_w_available()))  //  等待设备可用。如果等待过程中有异步信号,返回-ERESTARTSYS
             return   - ERESTARTSYS;  /*  tell the fs layer to handle it  */
        spin_lock(
& scull_w_lock);
    }

    
//  和上面的"uid"设备一样了
     if  (scull_w_count  ==   0 )
        scull_w_owner 
=  current -> uid;  /*  grab it  */
    scull_w_count
++ ;
    spin_unlock(
& scull_w_lock);

    
/*  then, everything else is copied from the bare scull device  */
    
if  ((filp -> f_flags  &  O_ACCMODE)  ==  O_WRONLY)
        scull_trim(dev);
    filp
-> private_data  =  dev;
    
return   0 ;           /*  success  */
}

static   int  scull_w_release( struct  inode  * inode,  struct  file  * filp)
{
    
int  temp;

    
//  申请自旋锁,递减打开计数,释放自旋锁。
    spin_lock( & scull_w_lock);
    scull_w_count
-- ;
    temp 
=  scull_w_count;
    spin_unlock(
& scull_w_lock);

    
//  如果打开计数是0,唤醒scull_w_wait信号
     if  (temp  ==   0 )
        wake_up_interruptible_sync(
& scull_w_wait);  /*  awake other uid's  */
    
return   0 ;
}


/*
 * The other operations for the device come from the bare device
 
*/
struct  file_operations scull_wusr_fops  =  {
    .owner 
=       THIS_MODULE,
    .llseek 
=      scull_llseek,
    .read 
=        scull_read,
    .write 
=       scull_write,
    .ioctl 
=       scull_ioctl,
    .open 
=        scull_w_open,
    .release 
=     scull_w_release,
};

/* ***********************************************************************
 *
 * Finally the `cloned' private device. This is trickier because it
 * involves list management, and dynamic allocation.
 
*/
 
//  最后是被克隆的私有设备。
 
//  这是棘手的,因为涉及列表管理和动态分配。

/*  The clone-specific data structure includes a key field  */

struct  scull_listitem {
    
struct  scull_dev device;
    dev_t key;
    
struct  list_head list;
    
};

//  设备列表,和保护列表的自旋锁
/*
 The list of devices, and a lock to protect it  */
static  LIST_HEAD(scull_c_list);  //  宏,声明并初始化一个list_head为scull_c_list的静态列表
static  spinlock_t scull_c_lock  =  SPIN_LOCK_UNLOCKED;

/*  A placeholder scull_dev which really just holds the cdev stuff.  */
//  占位符 scull_dev,实际只有cdev结构 TODO 没看懂。。
static   struct  scull_dev scull_c_device;   

/*  Look for a device or create one if missing  */
//  查找指定key的设备,如果没有找到就创建一个新的
static   struct  scull_dev  * scull_c_lookfor_device(dev_t key)
{
    
struct  scull_listitem  * lptr;

    
//  遍历scull_listitem,查找指定key的设备
    
//  结构 struct scull_listitem lptr 中的成员struct list_head list组成了一个链表,链表头是struct list_head scull_c_list
    list_for_each_entry(lptr,   //  pos: the type * to use as a loop cursor
                         & scull_c_list,  //  head: 要遍历的链表头
                        list) {         //  member: the name of the list_struct within the struct
         if  (lptr -> key  ==  key)
            
return   & (lptr -> device);  //  返回scull_dev 成员
    }

    
/*  not found  */
    
//  如果没有找到指定key的设备,创建一个scull_listitem结构,加在链表中,返回scull_dev成员
    lptr  =  kmalloc( sizeof ( struct  scull_listitem), GFP_KERNEL);
    
if  ( ! lptr)
        
return  NULL;

    
/*  initialize the device  */
    memset(lptr, 
0 sizeof ( struct  scull_listitem));
    lptr
-> key  =  key;
    scull_trim(
& (lptr -> device));  /*  initialize it  */  
    init_MUTEX(
& (lptr -> device.sem));

    
/*  place it in the list  */
    list_add(
& lptr -> list,  & scull_c_list);

    
return   & (lptr -> device);
}

static   int  scull_c_open( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_dev  * dev;
    dev_t key;

    
//  如果当前设备没有终端,返回-EINVAL(参数无效)
     if  ( ! current -> signal -> tty) { 
        PDEBUG(
" Process \ " % s\ "  has no ctl tty\n " , current -> comm);
        
return   - EINVAL;
    }

    
//  获得当前终端的设备号保存到key中
    key  =  tty_devnum(current -> signal -> tty);

    
/*  look for a scullc device in the list  */
    
//  从 scull_listitem 中获取指定key的设备(如果没有就创建一个新的返回)-同一终端(key)共用一个设备
    spin_lock( & scull_c_lock);
    dev 
=  scull_c_lookfor_device(key);
    spin_unlock(
& scull_c_lock);

    
if  ( ! dev)
        
return   - ENOMEM;

    
/*  then, everything else is copied from the bare scull device  */
    
if  ( (filp -> f_flags  &  O_ACCMODE)  ==  O_WRONLY)
        scull_trim(dev);
    filp
-> private_data  =  dev;
    
return   0 ;           /*  success  */
}

static   int  scull_c_release( struct  inode  * inode,  struct  file  * filp)
{
    
/*
     * Nothing to do, because the device is persistent.
     * A `real' cloned device should be freed on last close
     
*/
    
return   0 ;
}



/*
 * The other operations for the device come from the bare device
 
*/
struct  file_operations scull_priv_fops  =  {
    .owner 
=     THIS_MODULE,
    .llseek 
=    scull_llseek,
    .read 
=      scull_read,
    .write 
=     scull_write,
    .ioctl 
=     scull_ioctl,
    .open 
=      scull_c_open,
    .release 
=   scull_c_release,
};

/* ***********************************************************************
 *
 * And the init and cleanup functions come last
 
*/

static   struct  scull_adev_info {
    
char   * name;
    
struct  scull_dev  * sculldev;
    
struct  file_operations  * fops;
} scull_access_devs[] 
=  {
    { 
" scullsingle " & scull_s_device,  & scull_sngl_fops },
    { 
" sculluid " & scull_u_device,  & scull_user_fops },
    { 
" scullwuid " & scull_w_device,  & scull_wusr_fops },
    { 
" sullpriv " & scull_c_device,  & scull_priv_fops }
};
#define  SCULL_N_ADEVS 4

/*
 * Set up a single device.
 
*/
 
//  给定dev_t和scull_adev_info,建立一个设备
static   void  scull_access_setup (dev_t devno,  struct  scull_adev_info  * devinfo)
{
    
struct  scull_dev  * dev  =  devinfo -> sculldev;
    
int  err;

    
/*  Initialize the device structure  */
    
//  初始化scull_dev结构
    dev -> quantum  =  scull_quantum;
    dev
-> qset  =  scull_qset;
    init_MUTEX(
& dev -> sem);

    
/*  Do the cdev stuff.  */
    
//  cdev_init 初始化一个cdev结构
    cdev_init( & dev -> cdev,   //  struct cdev *cdev: 要初始化的结构
              devinfo -> fops);   //  const struct file_operations *fops: 该设备的 file_operations
              
    
//  kobject_set_name 给kobject设置名字
    kobject_set_name( & dev -> cdev.kobj,   //  struct kobject *kobj: 要设置名字的结构 
                    devinfo -> name);      //  const char *fmt: 用来创建名字的格式化字符串
    dev -> cdev.owner  =  THIS_MODULE;
    
    
//  cdev_add() 给系统中添加一个字符设备 &dev->cdev。立即生效。出错时返回一个负的错误码。
    err  =  cdev_add ( & dev -> cdev,      //  struct cdev *p:  设备的cdev结构
                    devno,           //  dev_t dev: 设备的第一个设备号
                     1 );              //  unsigned count: 设备对应的连续次设备号
         /*  Fail gracefully if need be  */
    
    
//  如果添加设备出错,dev->cdev.kobject引用计数减1
     if  (err) {
        printk(KERN_NOTICE 
" Error %d adding %s\n " , err, devinfo -> name);
        
//  kobject_put 递减设备的引用计数。如果引用计数为0,调用 kobject_cleanup()
        kobject_put( & dev -> cdev.kobj);   //  struct kobject *kobj
    }  else
        printk(KERN_NOTICE 
" %s registered at %x\n " , devinfo -> name, devno);
}


int  scull_access_init(dev_t firstdev)
{
    
int  result, i;

    
/*  Get our number space  */
    
//  注册 SCULL_N_ADEVS 个设备号
    
//  register_chrdev_region() 注册一系列设备号。成功返回0,失败返回一个负的错误码。
    result  =  register_chrdev_region (firstdev,       //  dev_t from: 要注册的第一个设备号,必须包含主设备号。 
                                     SCULL_N_ADEVS,      //  unsigned count: 需要的连续设备号个数
                                      " sculla " );          //  const char *name: 设备或驱动名称
     if  (result  <   0 ) {
        printk(KERN_WARNING 
" sculla: device number registration failed\n " );
        
return   0 ;
    }
    scull_a_firstdev 
=  firstdev;

    
/*  Set up each device.  */
    
//  循环给系统中添加 SCULL_N_ADEVS 个字符设备
     for  (i  =   0 ; i  <  SCULL_N_ADEVS; i ++ )
        scull_access_setup (firstdev 
+  i, scull_access_devs  +  i);
    
return  SCULL_N_ADEVS;
}

/*
 * This is called by cleanup_module or on failure.
 * It is required to never fail, even if nothing was initialized first
 
*/
void  scull_access_cleanup( void )
{
    
struct  scull_listitem  * lptr,  * next;
    
int  i;

    
/*  Clean up the static devs  */
    
//  遍历清除scull_acess_dev中每个静态设备: 移除cdev,释放sculldev数据区
     for  (i  =   0 ; i  <  SCULL_N_ADEVS; i ++ ) {
        
struct  scull_dev  * dev  =  scull_access_devs[i].sculldev;
        
//  从系统中删除dev->cdev
        
//  cdev_del() 从系统中移除一个 cdev ,可能要自己释放结构
        cdev_del( & dev -> cdev);   //  struct cdev *p: 要移除的cdev结构
        scull_trim(scull_access_devs[i].sculldev);
    }

        
/*  And all the cloned devices  */
    
//  遍历清除 scull_listitem中每个cloned设备: 删除list_head,释放sculldev数据区,释放scull_listitem结构内存
    
//  list_for_each_entry_safe 要求调用者另外提供一个与pos(lptr)同类型的指针next,在for循环中暂存pos的下一个节点的地址,避免因pos节点被释放而造成的断链
    list_for_each_entry_safe(lptr, next,  & scull_c_list, list) { 
        
//  list_del 删除链表的entry
        list_del( & lptr -> list);   //  struct list_head *entry: 要从链表中删除的元素
        scull_trim( & (lptr -> device));
        kfree(lptr);
    }

    
/*  Free up our number space  */
    
//  unregister_chrdev_region 解注册从scull_a_firstdev开始的连续SCULL_N_ADEVS个设备
    unregister_chrdev_region(scull_a_firstdev,   //  dev_t from 
                            SCULL_N_ADEVS);      //  unsigned count
     return ;
}


pipe.c
/*
 * pipe.c -- fifo driver for scull
 *
 * 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 
< linux / module.h >
#include 
< linux / moduleparam.h >

#include 
< linux / kernel.h >      /*  printk(), min()  */
#include 
< linux / slab.h >          /*  kmalloc()  */
#include 
< linux / fs.h >          /*  everything  */
#include 
< linux / proc_fs.h >
#include 
< linux / errno.h >      /*  error codes  */
#include 
< linux / types.h >      /*  size_t  */
#include 
< linux / fcntl.h >
#include 
< linux / poll.h >
#include 
< linux / cdev.h >
#include 
< asm / uaccess.h >

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

struct  scull_pipe {
        wait_queue_head_t inq, outq;       
/*  read and write queues  */   //  wait_queue_head_t 等待队列头
         char   * buffer,  * end;                 /*  begin of buf, end of buf  */
        
int  buffersize;                     /*  used in pointer arithmetic  */
        
char   * rp,  * wp;                      /*  where to read, where to write  */
        
int  nreaders, nwriters;             /*  number of openings for r/w  */
        
struct  fasync_struct  * async_queue;  /*  asynchronous readers  */   //  异步操作的文件指针结构 TODO
         struct  semaphore sem;               /*  mutual exclusion semaphore  */
        
struct  cdev cdev;                   /*  Char device structure  */
};

/*  parameters  */
static   int  scull_p_nr_devs  =  SCULL_P_NR_DEVS;     /*  number of pipe devices  */
int  scull_p_buffer  =   SCULL_P_BUFFER;     /*  buffer size  */
dev_t scull_p_devno;            
/*  Our first device number  */

//  模块加载时,指定管道设备数量、缓存大小
module_param(scull_p_nr_devs,  int 0 );     /*  FIXME check perms  */
module_param(scull_p_buffer, 
int 0 );

static   struct  scull_pipe  * scull_p_devices;

static   int  scull_p_fasync( int  fd,  struct  file  * filp,  int  mode);
static   int  spacefree( struct  scull_pipe  * dev);
/*
 * Open and close
 
*/


static   int  scull_p_open( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_pipe  * dev;

    
//  获取和inode->i_cdev对应的管道设备(scull_pipe结构)指针
    
//  container_of: cdev嵌套在struct scull_pipe中,由指向cdev成员的指针,获得指向struct scull_pipe的指针
    dev  =  container_of(inode -> i_cdev,   //  ptr: 指向成员的指针
                        struct  scull_pipe,    //  type: 容器结构的类型
                       cdev);            //  member: 成员名称
    filp -> private_data  =  dev;

    
if  (down_interruptible( & dev -> sem))
        
return   - ERESTARTSYS;
    
//  如果dev->buffer为NULL,申请缓存
     if  ( ! dev -> buffer) {
        
/*  allocate the buffer  */
        dev
-> buffer  =  kmalloc(scull_p_buffer, GFP_KERNEL);
        
if  ( ! dev -> buffer) {
            up(
& dev -> sem);
            
return   - ENOMEM;
        }
    }
    
//  给dev成员设初值
    dev -> buffersize  =  scull_p_buffer;
    dev
-> end  =  dev -> buffer  +  dev -> buffersize;
    dev
-> rp  =  dev -> wp  =  dev -> buffer;  /*  rd and wr from the beginning  */

    
/*  use f_mode,not  f_flags: it's cleaner (fs/open.c tells why)  */
    
//  递增设备打开计数,区分读写模式。
     if  (filp -> f_mode  &  FMODE_READ)
        dev
-> nreaders ++ ;
    
if  (filp -> f_mode  &  FMODE_WRITE)
        dev
-> nwriters ++ ;
    up(
& dev -> sem);

    
//  通知内核设备不支持移位llseek 
     return  nonseekable_open(inode, filp);
}



static   int  scull_p_release( struct  inode  * inode,  struct  file  * filp)
{
    
struct  scull_pipe  * dev  =  filp -> private_data;

    
/*  remove this filp from the asynchronously notified filp's  */
    scull_p_fasync(
- 1 , filp,  0 );  //  TODO
    down( & dev -> sem);
    
//  设备打开计数减1。打开计数为0时清空设备缓存。
     if  (filp -> f_mode  &  FMODE_READ)
        dev
-> nreaders -- ;
    
if  (filp -> f_mode  &  FMODE_WRITE)
        dev
-> nwriters -- ;
    
if  (dev -> nreaders  +  dev -> nwriters  ==   0 ) {
        kfree(dev
-> buffer);
        dev
-> buffer  =  NULL;  /*  the other fields are not checked on open  */
    }
    up(
& dev -> sem);
    
return   0 ;
}

/*
 * Data management: read and write
 
*/

static  ssize_t scull_p_read ( struct  file  * filp,  char  __user  * buf, size_t count,
                loff_t 
* f_pos)
{
    
struct  scull_pipe  * dev  =  filp -> private_data;

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

    
//  如果缓冲区空(读写指针重合),循环等待缓冲区中有数据可读。
     while  (dev -> rp  ==  dev -> wp) {  /*  nothing to read  */
        up(
& dev -> sem);  /*  release the lock  */
        
if  (filp -> f_flags  &  O_NONBLOCK)  //  非阻塞打开
             return   - EAGAIN;
        PDEBUG(
" \ " % s\ "  reading: going to sleep\n " , current -> comm);
        
if  (wait_event_interruptible(dev -> inq, (dev -> rp  !=  dev -> wp)))    //  异步信号
             return   - ERESTARTSYS;  /*  signal: tell the fs layer to handle it  */
        
/*  otherwise loop, but first reacquire the lock  */
        
if  (down_interruptible( & dev -> sem))       //  可被中断地获取信号量
             return   - ERESTARTSYS;
    }

    
//  数据读取到用户空间。最多读取到dev->end(线程非循环缓冲)
     /*  ok, data is there, return something  */
    
if  (dev -> wp  >  dev -> rp)
        count 
=  min(count, (size_t)(dev -> wp  -  dev -> rp));
    
else   /*  the write pointer has wrapped, return data up to dev->end  */
        count 
=  min(count, (size_t)(dev -> end  -  dev -> rp));
    
if  (copy_to_user(buf, dev -> rp, count)) {
        up (
& dev -> sem);
        
return   - EFAULT;
    }

    
//  读位置修改
    dev -> rp  +=  count;
    
if  (dev -> rp  ==  dev -> end)  //  如果读到缓存结尾了,读位置移至缓存开头
        dev -> rp  =  dev -> buffer;  /*  wrapped  */
    up (
& dev -> sem);

    
/*  finally, awake any writers and return  */
    wake_up_interruptible(
& dev -> outq);
    PDEBUG(
" \ " % s\ "  did read %li bytes\n " ,current -> comm, ( long )count);
    
return  count;
}

/*  Wait for space for writing; caller must hold device semaphore.  On
 * error the semaphore will be released before returning. 
*/
static   int  scull_getwritespace( struct  scull_pipe  * dev,  struct  file  * filp)
{
    
//  如果缓存已满
    
//  apacefree返回缓存中可写空间个数
     while  (spacefree(dev)  ==   0 ) {  /*  full  */
        
//  初始化等待队列
        DEFINE_WAIT(wait);   //  DEFINE_WAIT 创建和初始化一个等待队列。wait是队列入口项的名字。
        
        up(
& dev -> sem);
        
if  (filp -> f_flags  &  O_NONBLOCK)  //  如果文件在非阻塞模式,立即返回
             return   - EAGAIN;
        PDEBUG(
" \ " % s\ "  writing: going to sleep\n " ,current -> comm);
        
//  添加写等待队列入口,设置进程状态为可中断休眠
        
//  prepare_to_wait()添加等待队列入口到队列,并设置进程状态
        prepare_to_wait( & dev -> outq,   //  wait_queue_head_t *queue: 等待队列头
                         & wait,       //  wait_queue_t *wait: 进程入口
                        TASK_INTERRUPTIBLE);     //  int state: 进程新状态 TASK_INTERRUPTIBLE 可中断休眠
        
//  在检查确认仍然需要休眠之后,调用schedule
         if  (spacefree(dev)  ==   0 )
            schedule(); 
//  TODO 
        
//  条件满足退出后,确保状态为running,同时将进程从等待队列中删除。
        finish_wait( & dev -> outq,   //  wait_queue_head_t *queue
                     & wait);       //  wait_queue_t *wait
        
//  如果当前进程有信号处理,返回-ERESTARTSYS
         if  (signal_pending(current))  //  signal_pending 检查当前进程是否有信号处理,返回不为0表示有信号需要处理
             return   - ERESTARTSYS;  /*  signal: tell the fs layer to handle it  */
        
//  获取信号量,除非发生中断
        
//  down_iterruptible尝试获取信号量。如果没有更多任务被允许获取信号量,该任务投入睡眠等待;如果睡眠被信号打断,返回-EINTR;如果成功获取信号量,返回0
         if  (down_interruptible( & dev -> sem))
            
return   - ERESTARTSYS;
    }
    
return   0 ;
}    

/*  How much space is free?  */
//  获取dev空闲空间个数
static   int  spacefree( struct  scull_pipe  * dev)
{
    
if  (dev -> rp  ==  dev -> wp)
        
return  dev -> buffersize  -   1 ;
    
return  ((dev -> rp  +  dev -> buffersize  -  dev -> wp)  %  dev -> buffersize)  -   1 //  todo
}

static  ssize_t scull_p_write( struct  file  * filp,  const   char  __user  * buf, size_t count,
                loff_t 
* f_pos)
{
    
struct  scull_pipe  * dev  =  filp -> private_data;
    
int  result;

    
//  获取信号量
     if  (down_interruptible( & dev -> sem))
        
return   - ERESTARTSYS;

    
/*  Make sure there's space to write  */
    
//  如果没有空间可写,返回错误码
    result  =  scull_getwritespace(dev, filp);
    
if  (result)
        
return  result;  /*  scull_getwritespace called up(&dev->sem)  */

    
/*  ok, space is there, accept something  */
    count 
=  min(count, (size_t)spacefree(dev));  //  apacefree 获取空闲空间个数
     if  (dev -> wp  >=  dev -> rp)
        count 
=  min(count, (size_t)(dev -> end  -  dev -> wp));  /*  to end-of-buf  */
    
else   /*  the write pointer has wrapped, fill up to rp-1  */
        count 
=  min(count, (size_t)(dev -> rp  -  dev -> wp  -   1 ));
    PDEBUG(
" Going to accept %li bytes to %p from %p\n " , ( long )count, dev -> wp, buf);
    
if  (copy_from_user(dev -> wp, buf, count)) {
        up (
& dev -> sem);
        
return   - EFAULT;
    }
    dev
-> wp  +=  count;
    
if  (dev -> wp  ==  dev -> end)
        dev
-> wp  =  dev -> buffer;  /*  wrapped  */
    up(
& dev -> sem);

    
/*  finally, awake any reader  */
    wake_up_interruptible(
& dev -> inq);   /*  blocked in read() and select()  */

    
/*  and signal asynchronous readers, explained late in chapter 5  */
    
if  (dev -> async_queue)
        kill_fasync(
& dev -> async_queue, SIGIO, POLL_IN);
    PDEBUG(
" \ " % s\ "  did write %li bytes\n " ,current -> comm, ( long )count);
    
return  count;
}

static  unsigned  int  scull_p_poll( struct  file  * filp, poll_table  * wait)
{
    
struct  scull_pipe  * dev  =  filp -> private_data;
    unsigned 
int  mask  =   0 ;

    
/*
     * The buffer is circular; it is considered full
     * if "wp" is right behind "rp" and empty if the
     * two are equal.
     
*/
     
//  环形缓冲: wp在rp右侧表示缓冲区满;wp==rp表示空。
    
//  down() 检查dev->sem是否大于0.如果大于0,将值减1;如果等于0,进程投入睡眠.
    down( & dev -> sem);
    
//  把当前进程添加到wait参数指定的等待列表中
    poll_wait(filp,    //  struct file *filp
               & dev -> inq,   //  wait_queue_head_t * wait_address
              wait);     //  poll_table *p
    poll_wait(filp,  & dev -> outq, wait);
    
//  如果缓存不空,可读
     if  (dev -> rp  !=  dev -> wp)
        mask 
|=  POLLIN  |  POLLRDNORM;     /*  readable  */
    
//  如果缓存不满,可写
     if  (spacefree(dev))
        mask 
|=  POLLOUT  |  POLLWRNORM;     /*  writable  */
    
//  释放信号量
    up( & dev -> sem);
    
return  mask;
}


static   int  scull_p_fasync( int  fd,  struct  file  * filp,  int  mode)
{
    
struct  scull_pipe  * dev  =  filp -> private_data;

    
//  设置 fasync 队列。
    
//  fasync_helper()从相关的进程列表中添加或去除入口项。出错返回负数,没有改变返回0,添加删除entry返回正数。
     return  fasync_helper(fd, filp, mode,  & dev -> async_queue);
}

/*  FIXME this should use seq_file  */
#ifdef SCULL_DEBUG
static   void  scullp_proc_offset( char   * buf,  char   ** start, off_t  * offset,  int   * len)
{
    
if  ( * offset  ==   0 )
        
return ;
    
if  ( * offset  >=   * len) {     /*  Not there yet  */
        
* offset  -=   * len;
        
* len  =   0 ;
    }
    
else  {             /*  We're into the interesting stuff now  */
        
* start  =  buf  +   * offset;
        
* offset  =   0 ;
    }
}


static   int  scull_read_p_mem( char   * buf,  char   ** start, off_t offset,  int  count,
        
int   * eof,  void   * data)
{
    
int  i, len;
    
struct  scull_pipe  * p;

#define  LIMIT (PAGE_SIZE-200)    /* don't print any more after this size */
    
* start  =  buf;
    
//  len 记录读取字节数
    len  =  sprintf(buf,  " Default buffersize is %i\n " , scull_p_buffer);
    
//  遍历每一个管道设备,总输出长度不大于LIMIT
     for (i  =   0 ; i < scull_p_nr_devs  &&  len  <=  LIMIT; i ++ ) {
        p 
=   & scull_p_devices[i];
        
if  (down_interruptible( & p -> sem))  //  可中断的申请信号量
             return   - ERESTARTSYS;
        len 
+=  sprintf(buf + len,  " \nDevice %i: %p\n " , i, p);
/*         len += sprintf(buf+len, "   Queues: %p %p\n", p->inq, p->outq); */
        len 
+=  sprintf(buf + len,  "    Buffer: %p to %p (%i bytes)\n " , p -> buffer, p -> end, p -> buffersize);
        len 
+=  sprintf(buf + len,  "    rp %p   wp %p\n " , p -> rp, p -> wp);
        len 
+=  sprintf(buf + len,  "    readers %i   writers %i\n " , p -> nreaders, p -> nwriters);
        up(
& p -> sem);
        
//  TODO: 移动偏移量?
        scullp_proc_offset(buf, start,  & offset,  & len);
    }
    
* eof  =  (len  <=  LIMIT);
    
return  len;
}


#endif



/*
 * The file operations for the pipe device
 * (some are overlayed with bare scull)
 
*/
struct  file_operations scull_pipe_fops  =  {
    .owner 
=     THIS_MODULE,
    .llseek 
=     no_llseek,
    .read 
=         scull_p_read,
    .write 
=     scull_p_write,
    .poll 
=         scull_p_poll,
    .ioctl 
=     scull_ioctl,
    .open 
=         scull_p_open,
    .release 
=     scull_p_release,
    .fasync 
=     scull_p_fasync,
};


/*
 * Set up a cdev entry.
 
*/
static   void  scull_p_setup_cdev( struct  scull_pipe  * dev,  int  index)
{
    
int  err, devno  =  scull_p_devno  +  index;
    
    cdev_init(
& dev -> cdev,  & scull_pipe_fops);
    dev
-> cdev.owner  =  THIS_MODULE;
    err 
=  cdev_add ( & dev -> cdev, devno,  1 );
    
/*  Fail gracefully if need be  */
    
if  (err)
        printk(KERN_NOTICE 
" Error %d adding scullpipe%d " , err, index);
}



/*
 * Initialize the pipe devs; return how many we did.
 
*/
 
//  初始化管道设备; 返回管道设备个数。
int  scull_p_init(dev_t firstdev)
{
    
int  i, result;

    
//  注册一系列设备号。成功返回0,失败返回一个负的错误码。
    result  =  register_chrdev_region(firstdev,   //  dev_t from: 要注册的第一个设备号,必须包含主设备号
                                    scull_p_nr_devs,     //  unsigned count: 需要的连续设备号个数
                                     " scullp " );           //  const char *name: 设备或驱动名称
     if  (result  <   0 ) {
        printk(KERN_NOTICE 
" Unable to get scullp region, error %d\n " , result);
        
return   0 ;
    }
    scull_p_devno 
=  firstdev;   //  第一个设备号
    
//  申请scull_p_nr_devs个scull_pipe设备的存储空间
    scull_p_devices  =  kmalloc(scull_p_nr_devs  *   sizeof ( struct  scull_pipe), GFP_KERNEL);
    
if  (scull_p_devices  ==  NULL) {
        unregister_chrdev_region(firstdev, scull_p_nr_devs);
        
return   0 ;
    }
    memset(scull_p_devices, 
0 , scull_p_nr_devs  *   sizeof ( struct  scull_pipe));
    
for  (i  =   0 ; i  <  scull_p_nr_devs; i ++ ) {
        
//  初始化读写等待队列头
        init_waitqueue_head( & (scull_p_devices[i].inq)); 
        init_waitqueue_head(
& (scull_p_devices[i].outq));
        
//  初始化互斥信号量,sem置1
        init_MUTEX( & scull_p_devices[i].sem);
        
//  建立字符设备入口
        scull_p_setup_cdev(scull_p_devices  +  i, i);
    }
#ifdef SCULL_DEBUG
    create_proc_read_entry(
" scullpipe " 0 , NULL, scull_read_p_mem, NULL);
#endif
    
return  scull_p_nr_devs;
}

/*
 * This is called by cleanup_module or on failure.
 * It is required to never fail, even if nothing was initialized first
 
*/
void  scull_p_cleanup( void )
{
    
int  i;

#ifdef SCULL_DEBUG
    remove_proc_entry(
" scullpipe " , NULL);
#endif

    
if  ( ! scull_p_devices)
        
return /*  nothing else to release  */

    
for  (i  =   0 ; i  <  scull_p_nr_devs; i ++ ) {
        cdev_del(
& scull_p_devices[i].cdev);
        kfree(scull_p_devices[i].buffer);
    }
    kfree(scull_p_devices);
    unregister_chrdev_region(scull_p_devno, scull_p_nr_devs);
    scull_p_devices 
=  NULL;  /*  pedantic  */
}
http://linux.chinaunix.net/bbs/thread-967565-1-1.html
http://www.ibm.com/developerworks/cn/linux/l-proc.html

 

源码:/Files/momoxiao/scull.zip

你可能感兴趣的:(scull源码分析 //未完待续,囧)