Chinaunix首页 | 论坛 | 博客
  • 博客访问: 470249
  • 博文数量: 105
  • 博客积分: 2258
  • 博客等级: 大尉
  • 技术积分: 922
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-23 13:21
文章分类

全部博文(105)

文章存档

2013年(1)

2012年(10)

2011年(94)

分类: LINUX

2011-09-05 17:54:58

      从外到内一步一步讲吧!
一、初始化和退出模块
这个没什么讲的了,就是告诉内核是哪个函数负责这个功能的。
module_init(scull_init_module);
module_exit(scull_cleanup_module);
初始化模块的函数
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);
        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 ", scull_major);
        return result;
    }

        /*
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     */
//这里为scull_dev设备分配空间一共分配4个scull_dev大小的空间因为有4个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));
//初始化每一个设备
        /* Initialize each device. */
    for (i = 0; i < scull_nr_devs; i++) {
        scull_devices[i].quantum = scull_quantum;//当前的分配尺寸,是1000
        scull_devices[i].qset = scull_qset;//当前的数组大小,是4000字节
        init_MUTEX(&scull_devices[i].sem);//互斥信号量,这章好像还没用上。
        scull_setup_cdev(&scull_devices[i], i);//字符设备注册
    }
//其他的相关scull设备初始化和本章无关。
        /* 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;
}

注销模块函数
void scull_cleanup_module(void)
{
    int i;
    dev_t devno = MKDEV(scull_major, scull_minor);//得到设备号,如果注册时候有设备号的情况。

    /* 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);//删除字符设备
        }
        kfree(scull_devices);//释放字符设备结构占用的空间
    }

#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);
//删除其他的scull相关设备,也就是前面提到的其他的scull版本
    /* and call the cleanup functions for friend devices */
    scull_p_cleanup();
    scull_access_cleanup();

}
二、open和release函数
int scull_open(struct inode *inode, struct file *filp)
{
    struct scull_dev *dev; /* device information *///首先声明了一个scull_dev设备的指针

    dev = container_of(inode->i_cdev, struct scull_dev, cdev);//通过inode的i_cdev结构也就是cdev结构我们可以得到自己定义的scull_dev结构指针。
    filp->private_data = dev; /* for other methods *///将找到的指针保存到file结构中的private_data字段中,用以备用。

    /* 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;
        scull_trim(dev); /* ignore errors *///如果以只写方式打开的话对结构清零。
        up(&dev->sem);
    }
    return 0;          /* success */
}

int scull_release(struct inode *inode, struct file *filp)
{
    return 0;
}
三、读写函数
ssize_t scull_read(struct file *filp, char __user *buf, size_t count,
                loff_t *f_pos)
{
    struct scull_dev *dev = filp->private_data; //定义一个scull_dev设备类型指针指向在open函数找到的scull_dev设备类型保存在file的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 *///每个链表项的总字节数这里是4000*1000
    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)//如果偏移量加读取的数量大于设备结构的尺寸,那么吧读取数量改成只读到设备结构的最后为止。
        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);//从链表头一直找到链表的末端,如果中间的位置是NULL那就给它分配空间。

    if (dptr == NULL || !dptr->data || ! dptr->data[s_pos])
        goto out; /* don't fill holes */

    /* read only up to the end of this quantum */
//只读取到当前量子的末端。如果不是count所希望的,那就再次调用read读取下一个量子。
    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;
}
写函数
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;
//如果当前的首指针是NULL那就分配一个量子的空间4000字节
    if (!dptr->data) {
        dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
        if (!dptr->data)
            goto out;
        memset(dptr->data, 0, qset * sizeof(char *));
    }
//如果指向的需要拷贝数据的地方为NULL那就分配1000个数组指针
    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;
}
阅读(644) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~