ldd3代码分析(高级字符驱动)

    技术2022-05-11  82

    /* * main.c -- the bare scull char module * * 此代码为ldd3例子,自己加了些注释;希望可以和更多和我同样有兴趣的鸟儿们一块学习讨论。  * 哪有注释的不对的地方请发mail给我,或留言; * * author : liyangth@gmail.com  * * date: 2007-2-7 *  * Note:注释的每一个关键的段都以[tag00]作了标签,大家可以按照tag的顺序阅读; * e.g: 搜索 "Tag000" */ #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; /* * 模块参数,可在模块转载时赋值,很灵活方便; * e.g: *         insmod scull.ko scull_major=111 scull_nr_devs=3 scull_quantum=1000 * *[形参说明] * 1 -- 变量名; * 2 -- 变量类型; * 3 -- sysfs入口项的访问许可掩码(一般用S_IRUGO就成);*/ module_param(scull_major,  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 */ /* Note: 不要把它理解成一个指向scull_dev结构的指针, 它其实是一个scull_dev结构数组,等待下面kmalloc分配多个我们scull设备空间 */ /* * 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;    for (dptr = dev->data; dptr; dptr = next) /* all the list items */        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 = scull_quantum;    dev->qset = scull_qset;    dev->data = NULL;    return 0;} // Start: [Tag003] proc的实现,可以先不看; #ifdef SCULL_DEBUG  /* use proc only if debugging */ /* * The proc filesystem: function to read and entry */ int  scull_read_procmem( char   * buf,  char   ** start, off_t offset,                    int  count,  int   * eof,  void   * data) {    int i, j, len = 0;    int limit = count - 80/* Don't print more than this */    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," Device %i: qset %i, q %i, sz %li ",                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 ",                    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 ",                                j, qs->data[j]);                }        }        up(&scull_devices[i].sem);    }    *eof = 1;    return len;} /* * 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. */ 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;} 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 */} 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, " Device %i: qset %i, q %i, sz %li ",            (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 ", 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 ",                            i, d->data[i]);            }    }    up(&dev->sem);    return 0;}      /* * 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} ; /* * 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);} /* * 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). */ static   void  scull_create_proc( void ) {    struct proc_dir_entry *entry;    create_proc_read_entry("scullmem"0 /* default mode */,            NULL /* parent dir */, scull_read_procmem,            NULL /* client data */);    entry = create_proc_entry("scullseq"0, NULL);    if (entry)        entry->proc_fops = &scull_proc_ops;} static   void  scull_remove_proc( void ) {    /* no problem if it was not registered */    remove_proc_entry("scullmem", NULL /* parent dir */);    remove_proc_entry("scullseq", NULL);} #endif  /* SCULL_DEBUG */ // End /* 开始实现对设备操作的方法集了,关键!!! */ /* * Open and close */ // [Tag004] /*open应完成的工作有:    1.检查设备特定的错误(诸如设备未就绪或类似的硬件问题)    2.如果设备是首次打开,则对其进行初始化;    3.如有必要,更新f_op指针;    4.分配并填写filp->private_data;(在这里我们只实现这项即可)*/ /*[形参说明]    struct inode *inode -- 用它的i_cdev成员得到dev;    struct file *filp -- 将得到的dev存放到他的成员private_data中;*/ 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);    /*    [说明]        1.我们要填充的应该是我们自己的特殊设备,而不是钳在他里面的字符设备结构;        2.inode结构的i_cdev成员这能提供基本字符设备结构;        3.这里利用了定义在<linux/kernel.h>中的宏来实现通过cdev得到dev;    */        /*    以后read , write ,等操作的实现中就靠他来得到dev了;    */    filp->private_data = dev; /* for other methods */        /* 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 */} /* close device file, in here we do nothing */ /*  * [Tag005] * close应完成的工作有: *    1.释放由open分配的,保存在filp->private_data中的所有内容; *  2.在最后一次关闭操作时关闭设备; * [注意:]并不是每次的close系统调用都会去调用到release. 在open时,也仅在open时才会创建 * 一个新的数据结构;在fork, dup时只是增加了这个结构中维护的一个引用计数; * 所以当这个引用计数为0时,调用的close才意味着要释放设备数据结构,此时release才会被调用; */ int  scull_release( struct  inode  * inode,  struct  file  * filp) {    return 0;} /* * Follow the list *  * 第一次调用时用于创建链表; * 然后就是找到第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, 0sizeof(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, 0sizeof(struct scull_qset));        }        qs = qs->next;        continue;    }    return qs;} /*[Tag006] * Data management: read and write * [read和write的参数] *        1] filp -- 文件指针;用它的成员filp->private_data得到dev; *         2] buf -- 都是来自用户空间的指针; *      3] count -- 缓冲区大小;(希望传输的字节数目) *        4] f_pos -- 指向一个长偏移量对象的指针,这个对象指明了用户在文件中进行存取 *            操作的位置; * *[返回值] *         1]如果返回值等于count,则完成了所请求数目的字节传输; *        2]如果返回值是正,但小于count,则继续读或写余下的数据; *        3]如果为0,则证明已经到了文件尾; *        4]如果为负,则发生了错误。会返回一个错误码,该值指明了发生了什么错误。 *             错误码在<linux/errno.h>中定义; *            例如:-EINTR (系统调用被中断) *                  -EFAULT (无效地址) */ 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;    /* 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;    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; //在这个链表项的什么位置(偏移量),用于下面找qset索引和偏移量;    s_pos = rest / quantum;        //在这个节点里**data这个指针数组的第几行;     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])        goto out/* don't fill holes *///以一个量子为单位传,简化了代码;    /* read only up to the end of this quantum */    if (count > quantum - q_pos)        count = quantum - q_pos;/* * 上面为这步准备了具体在哪个链表项的指针数组的第几行的第几列(即dptr->data[s_pos] + q_pos) * 从这个位置的内核态的buf中拷给用户态    */    //关键一步,将数据拷给用户空间    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;} // 与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;} /* * 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()     */    if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;    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     */    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) {      case SCULL_IOCRESET:        scull_quantum = SCULL_QUANTUM;        scull_qset = SCULL_QSET;        break;              case SCULL_IOCSQUANTUM: /* Set: arg points to the value */        if (! capable (CAP_SYS_ADMIN))            return -EPERM;        retval = __get_user(scull_quantum, (int __user *)arg);        break;      case SCULL_IOCTQUANTUM: /* Tell: arg is the value */        if (! capable (CAP_SYS_ADMIN))            return -EPERM;        scull_quantum = arg;        break;      case SCULL_IOCGQUANTUM: /* Get: arg is pointer to result */        retval = __put_user(scull_quantum, (int __user *)arg);        break;      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;              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 < 0return -EINVAL;    filp->f_pos = newpos;    return newpos;} // [Tag007]将这组操作打包为一个对象; 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 */ // [Tag008]模块卸载或goto fail时; /* * 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);    /* 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);    /* and call the cleanup functions for friend devices */    scull_p_cleanup();    scull_access_cleanup();} /* [Tag002]     这里主要干了2件事;    在内核内部使用struct cdev结构来表示字符设备;    [1]在这里因为我们将cdev结构嵌入到自己的scull_dev设备下了,所以我们用下面这个方法来    初始化已分配的结构;    cdev_init(&dev->cdev, &scull_fops);        [2]告诉内核我们新结构的信息;*/ /* * 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);       // [1]    cdev_init(&dev->cdev, &scull_fops);    /* 初始化, 字符设备和给它一组在它上面操作的方法集 */        /* 填充基本字符设备的成员 */    dev->cdev.owner = THIS_MODULE;        //模块计数    dev->cdev.ops = &scull_fops;        //附上一组操作自己的方法集    //    [2]    err = cdev_add (&dev->cdev, devno, 1);    /*    函数说明:        cdev -- 字符设备的结构指针,我们就是要把他告诉给内核;        devno -- 设备编号,用MKDEV利用全局的主设备号和次设备号生成的;        1    -- 是应该和该设备关联的设备编号的数量, 一般情况下都为1;            一般我们都是一个设备编号对应一个设备;            */    /*    注意:        在调用cdev_add后,我们的设备就被添加到系统了,他"活"了. 附加的操作集也就可以被内核调用了        ,因此,在驱动程序还没有完全准备好处理设备上的操作时,就不能调用cdev_add!    */    /* Fail gracefully if need be */    if (err)        printk(KERN_NOTICE "Error %d adding scull%d", err, index);} /*[Tag000] * 当模块加载时,调用;但是为什么要放在最后来实现他呢,看到Tag002时,你应该就明白了;*/ int  scull_init_module( void ) {    int result, i;    dev_t dev = 0;/* [Tag001] *//* [1]分配设备编号 *//* * 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); /* 利用主设备号,找到设备编号给方法1用 */        result = register_chrdev_region(dev, scull_nr_devs, "scull");    } else {        /* 动态自己生成设备编号,然后再利用设备编号得到主设备号;                        记住如果用这个方法那么就要后建设备文件了,因为不能提前知道主号                        当然也可以利用ldd3书中提供的脚本,巨方便&&通用 */        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;    }    /*[2]设备对象实例化*/         /*      * allocate the devices -- we can't have them static, as the number     * can be specified at load time     */    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));/* [3]在这里初始化设备用了2.6的新方法,在scull_setup_cdev里完成 */        /* Initialize each device. */    for (i = 0; i < scull_nr_devs; i++{        scull_devices[i].quantum = scull_quantum;    /* 可以根据自己insmod时传参                                                        来自己改变量子和量子集(指针数组)的大小 */        scull_devices[i].qset = scull_qset;        init_MUTEX(&scull_devices[i].sem);        scull_setup_cdev(&scull_devices[i], i);    /* 在分别完主设备编号后goto Tag002 设备注册 */    }        /* 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);         // insmod     module_exit(scull_cleanup_module);     // rmmod

     

    /* * 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  PDEBUG             /* undef it, just in case */ #ifdef SCULL_DEBUG#  ifdef __KERNEL__      /* This one if debugging is on, and kernel space */ #    define PDEBUG(fmt, args...) printk( KERN_DEBUG  " scull:  "  fmt, ## args)#   else       /* 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 #define  SCULL_MAJOR 0   /* dynamic major by default */ #endif #ifndef SCULL_NR_DEVS #define  SCULL_NR_DEVS 4    /* scull0 through scull3 */ #endif #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        /* 每个指针(量子)指向一个4000字节的区域 */ #endif #ifndef SCULL_QSET #define  SCULL_QSET    1000        /* 一个有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;    /* 下一个链表节点(链表项) */} ; /* 我们自己的设备(包含了基本的cdev字符设备结构) */ 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        */} ; /* * 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 */ /* 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_ */

     

    linux设备驱动(第三版)的例子里还提供了几个非常通用和灵活的脚本,还有一个标准的Makefile.

    大家可以利用下面的文件,修改一下运行试试效果。如果想了解读写的具体过程可以试试用strace命令来追踪;

    e.g : strace ls -l >/dev/scull0

     

    # Comment / uncomment the following line to disable / enable debugging#非常标准的Makefile,稍加修改就可以用在很多驱动上#将这个开关打开,看proc的输出。在这个例子分别有二个用于输出的proc文件。一个是用老方法实现的# / proc / scullmem#新方法  / proc / scullseqDEBUG  =  y    # Add your debugging flag (or not) to CFLAGSifeq ($(DEBUG),y)  DEBFLAGS  =   - - - DSCULL_DEBUG #  " -O "   is  needed to expand inlines else   DEBFLAGS  =   - O2endifCFLAGS  +=  $(DEBFLAGS)CFLAGS  +=   - I$(LDDINC)ifneq ($(KERNELRELEASE),)# call from kernel build systemscull - objs : =  main.o pipe.o access.oobj - m    : =  scull.o else KERNELDIR  ?=   / lib / modules / $(shell uname  - r) / buildPWD       : =  $(shell pwd)modules:    $(MAKE)  - C $(KERNELDIR) M = $(PWD) LDDINC = $(PWD) / .. / include modulesendifclean:    rm  - rf  * .o  *~  core .depend . * .cmd  * .ko  * .mod.c .tmp_versionsdepend .depend dep:    $(CC) $(CFLAGS)  - * .c  >  .dependifeq (.depend,$(wildcard .depend))include .dependendif

     

    下面这个是load,因为在2.6的新方法中我们是先动态分配主设备号,然后再根据/proc/modules来建立设备文件的

    看下面这个脚本的实现就知道了。

     

    # !/ bin / sh# $Id: scull_load,v  1.4   2004 / 11 / 03   06 : 19 : 49  rubini Exp $module = " scull " device = " scull " mode = " 664 " # Group: since distributions  do  it differently, look  for  wheel or use staff if  grep  - ' ^staff: '   / etc / group; then    group = " staff " else     group = " wheel " fi# invoke insmod with all arguments we got# and use a pathname,  as  insmod doesn ' t look in . by default / sbin / insmod . / $module.ko $ *   ||  exit  1 # retrieve major numbermajor = $(awk  " /$2=="$module" {print /$1} "   / proc / devices)# Remove stale nodes and replace them, then give gid and perms# Usually the script  is  shorter, it ' s scull that has several devices in it. rm  - / dev / $ {device} [ 0 - 3 ]mknod  / dev / $ {device} 0  c $major  0 mknod  / dev / $ {device} 1  c $major  1 mknod  / dev / $ {device} 2  c $major  2 mknod  / dev / $ {device} 3  c $major  3 ln  - sf $ {device} 0   / dev / $ {device} chgrp $group  / dev / $ {device} [ 0 - 3 ] chmod $mode   / dev / $ {device} [ 0 - 3 ]rm  - / dev / $ {device} pipe[ 0 - 3 ]mknod  / dev / $ {device} pipe0 c $major  4 mknod  / dev / $ {device} pipe1 c $major  5 mknod  / dev / $ {device} pipe2 c $major  6 mknod  / dev / $ {device} pipe3 c $major  7 ln  - sf $ {device} pipe0  / dev / $ {device} pipechgrp $group  / dev / $ {device} pipe[ 0 - 3 ] chmod $mode   / dev / $ {device} pipe[ 0 - 3 ]rm  - / dev / $ {device} singlemknod  / dev / $ {device} single  c $major  8 chgrp $group  / dev / $ {device} singlechmod $mode   / dev / $ {device} singlerm  - / dev / $ {device} uidmknod  / dev / $ {device} uid   c $major  9 chgrp $group  / dev / $ {device} uidchmod $mode   / dev / $ {device} uidrm  - / dev / $ {device} wuidmknod  / dev / $ {device} wuid  c $major  10 chgrp $group  / dev / $ {device} wuidchmod $mode   / dev / $ {device} wuidrm  - / dev / $ {device} privmknod  / dev / $ {device} priv  c $major  11 chgrp $group  / dev / $ {device} privchmod $mode   / dev / $ {device} priv

     

    下面这个用于卸载

    # !/ bin / shmodule = " scull " device = " scull " # invoke rmmod with all arguments we got / sbin / rmmod $module $ *   ||  exit  1 # Remove stale nodesrm  - / dev / $ {device}   / dev / $ {device} [ 0 - 3 ] rm  - / dev / $ {device} privrm  - / dev / $ {device} pipe  / dev / $ {device} pipe[ 0 - 3 ]rm  - / dev / $ {device} singlerm  - / dev / $ {device} uidrm  - / dev / $ {device} wuid

     

     


    最新回复(0)