zoukankan      html  css  js  c++  java
  • ldd(linux设备驱动程序)实验2:scull

    按照ldd第三版书上的网址,下载下来的代码是最终版的,没有按章节剥离,不方便读者自己实验。

    以下是我手把手敲下的书上第二个实验scull的代码以及安装和测试的步骤。

    按上篇搭建linux驱动开发环境(配合LDD阅读)做完实验1后,scull的安装很简单了,在任意位置保存下面两个源文件Makefile,scull.c,执行make,生成目标模块scull.ko,然后insmod scull.ko。

    lsmod, 看到第一个就是scull,且会生成这个文件夹/sys/module/scull,可以进去看看里面有什么。cat /proc/devices   可以看到"xxx scull"。因为我的scull驱动(源码贴在下面)是随机生成设备号,所以要进去看。

    下面,我们就可以用mknod命令来申请设备文件了。

              mkdir  /dev/scull

               mknod /dev/scull/scull0 c xxx 0

        xxx必须是刚才在proc里看到的主设备号,此设备号是0。主、次设备号如果不符的话,这个命令不会执行失败,但是生成的设备在将来使用时会出这个错:scull0:No such device or address

          然后ls -li,看下新设备的主次设备号。

        到这步之后要注意力,如果你的驱动写得有问题,而编译时没有检测到的话,很可能在执行下面的步骤时使整个linux系统挂起,那只能切断虚拟机电源重新来过了。由于书上的scull代码极简,而官网下载的scull源码相对繁杂,我想用最简的来测试,书上的代码却不全,我只好半用书半用网上的源码,改了非常久。

        然后依据书上的“试试新设备”这一节来测试这个设备。可以用的命令有:cp, dd, 重新输入输出, free;还可以在驱动里写printk来跟踪测试。还可以用strace命令来监视系统调用。

    解释下,mknod 的标准形式为:       mknod DEVNAME {b | c}  MAJOR  MINOR

           1,DEVNAME是要创建的设备文件名,如果想将设备文件放在一个特定的文件夹下,就需要先用mkdir在dev目录下新建一个目录;

           2, b和c 分别表示块设备和字符设备:

                      b表示系统从块设备中读取数据的时候,直接从内存的buffer中读取数据,而不经过磁盘;

                      c表示字符设备文件与设备传送数据的时候是以字符的形式传送,一次传送一个字符,比如打印机、终端都是以字符的形式传送数据;

           3,MAJOR和MINOR分别表示主设备号和次设备号:

                 为了管理设备,系统为每个设备分配一个编号,一个设备号由主设备号和次设备号组成。主设备号标示某一种类的设备,次设备号用来区分同一类型的设备。 linux操作系统中为设备文件编号分配了32位无符号整数,其中前12位是主设备号,后20位为次设备号,所以在向系统申请设备文件时主设备号不好超过 4095,次设备号不好超过2^20 -1。

    模块装好后,书本自带的代码中有个scull/scull_load用以装载,但这也是混装了全书的架构,而我们要的是剥离出来的第三章的实验,如果想试一试,可以在实验完毕后(因为这会造成难以估计的混乱)运行它。

    Makefile

    ifneq ($(KERNELRELEASE),)
    obj-m := scull.o
    else
    PWD := $(shell pwd)
    KVER ?= $(shell uname -r)
    KDIR := /lib/modules/$(KVER)/build
    all:
        $(MAKE) -C $(KDIR) M=$(PWD)
    clean:
        rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.symvers *.order
    endif

    scull.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.
     *
     */

    #ifndef __KERNEL__
    #  define __KERNEL__
    #endif
    #ifndef MODULE
    #  define MODULE
    #endif

    //#include <linux/config.h>
    #include <linux/slab.h>   /* kmalloc() */
    #include <asm/uaccess.h>

    #include <linux/module.h>
    #include <linux/kernel.h>   /* printk() */
    #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 <asm/system.h>     /* cli(), *_flags */

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


    int scull_major;
    int scull_nr_devs;
    int scull_quantum;
    int scull_qset;


    // struct file_operations *scull_fop_array[]={
    //     &scull_fops,      /* type 0 */
    //     &scull_priv_fops, /* type 1 */
    //     &scull_pipe_fops,  type 2
    //     &scull_sngl_fops, /* type 3 */
    //     &scull_user_fops, /* type 4 */
    //     &scull_wusr_fops  /* type 5 */
    // };
    // #define SCULL_MAX_TYPE 5

    // static void scull_setup_cdev(struct scull_dev *dev, int index){
    //     int err, devno = MKDEV(scull_major, scull_minor + index);

    //     cdev_init(&dev->cdev, &scull_fops):
    //     dev->cdev.owner = THIS_MODULE;
    //     dev->cdev.ops = &scull_fops;
    //     err = cdev_add (&dev->cdev, devno, 1);
    //     if(err)
    //         printk(KERN_NOTICE "Error %d adding scull%d", err, index);
    // }

    struct cdev {
        struct kobject kobj;
        struct module *owner;
        const struct file_operations *ops;
        struct list_head list;
        dev_t dev;
        unsigned int count;
    };

    struct scull_dev{
        struct scull_qset *data;
        int quantum;
        int qset;
        unsigned long size;
        unsigned int access_key;
        struct semaphore sem;
        struct cdev cdev;
        struct scull_dev* next;
    }scull_dev;

    /* memory management */

    struct scull_qset{
        void **data;
        struct scull_qset* next;
    };


    int scull_trim(struct scull_dev* dev){
        struct scull_qset *next, *dptr;
        int qset = dev->qset;
        int i;
        for(dptr = dev->data; dptr; dptr = next){
            if(dptr->data){
                for(i=0; i<qset; i++){
                    kfree(dptr->data[i]);
                }
                kfree(dptr->data);
                dptr->data = NULL;
            }
            next = dptr->next;
            kfree(dptr);
        }
        dev->size = 0;
        dev->quantum = scull_quantum;
        dev->qset = scull_qset;
        dev->data = NULL;
        return 0;
    }


    /*
     * Open and close
     */


    int scull_open(struct inode *inode, struct file *filp){
        struct scull_dev *dev; /* device information */

        dev = container_of(inode->i_cdev, struct scull_dev, cdev);
        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){
            scull_trim(dev); /* ignore errors */
        }
        return 0;   /* success */
    }

    int scull_release(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    /*
     * Follow the list
     */
    struct scull_dev *scull_follow(struct scull_dev *dev, int n)
    {
        while (n--) {
            if (!dev->next) {
                dev->next = kmalloc(sizeof(scull_dev), GFP_KERNEL);
                memset(dev->next, 0, sizeof(scull_dev));
            }
            dev = dev->next;
            continue;
        }
        return dev;
    }

    /*
     * Data management: read and write
     */

    ssize_t scull_read(struct file *filp, char *buf, size_t count,
                    loff_t *f_pos)
    {
        struct scull_dev *dev = filp->private_data; /* the first listitem */
        struct scull_qset *dptr;
        int quantum = dev->quantum;
        int qset = dev->qset;
        int itemsize = quantum * qset; /* how many bytes in the listitem */
        int item, s_pos, q_pos, rest;
        ssize_t ret = 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);

        if (!dptr->data)
            goto out; /* don't fill holes */
        if (!dptr->data[s_pos])
            goto out;
        /* read only up to the end of this quantum */
        if (count > quantum - q_pos)
            count = quantum - q_pos;

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

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

    ssize_t scull_write(struct file *filp, const char *buf, size_t count,
                    loff_t *f_pos)
    {
        struct scull_dev *dev = filp->private_data;
        struct scull_qset *dptr;
        int quantum = dev->quantum;
        int qset = dev->qset;
        int itemsize = quantum * qset;
        int item, s_pos, q_pos, rest;
        ssize_t ret = -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->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)) {
            ret = -EFAULT;
        goto out;
        }
        *f_pos += count;
        ret = count;

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

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


    /*
     * 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
     */
    static void scull_cleanup_module(void){
    //     int i;

    // #ifndef CONFIG_DEVFS_FS
    //     /* cleanup_module is never called if registering failed */
    //     unregister_chrdev(scull_major, "scull");
    // #endif

    // #ifdef SCULL_DEBUG /* use proc only if debugging */
    //     scull_remove_proc();
    // #endif
    //     if (scull_devices) {
    //         for (i=0; i<scull_nr_devs; i++) {
    //             scull_trim(scull_devices+i);
    //             /* the following line is only used for devfs */
    //             devfs_unregister(scull_devices[i].handle);
    //         }
    //         kfree(scull_devices);
    //     }

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

    //     /* once again, only for devfs */
    //     devfs_unregister(scull_devfs_dir);

    }


    static int scull_init_module(void){
    //     int result, i;

    //     SET_MODULE_OWNER(&scull_fops);
    // #ifdef CONFIG_DEVFS_FS
    //     /* If we have devfs, create /dev/scull to put files in there */
    //     scull_devfs_dir = devfs_mk_dir(NULL, "scull", NULL);
    //     if (!scull_devfs_dir) return -EBUSY; /* problem */

    // #else /* no devfs, do it the "classic" way  */    

    //     /*
    //      * Register your major, and accept a dynamic number. This is the
    //      * first thing to do, in order to avoid releasing other module's
    //      * fops in scull_cleanup_module()
    //      */
    //     result = register_chrdev(scull_major, "scull", &scull_fops);
    //     if (result < 0) {
    //         printk(KERN_WARNING "scull: can't get major %d\n",scull_major);
    //         return result;
    //     }
    //     if (scull_major == 0) scull_major = result; /* dynamic */

    // #endif /* CONFIG_DEVFS_FS */
    //     /*
    //      * 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(scull_dev), GFP_KERNEL);
    //     if (!scull_devices) {
    //         result = -ENOMEM;
    //         goto fail;
    //     }
    //     memset(scull_devices, 0, scull_nr_devs * sizeof(scull_dev));
    //     for (i=0; i < scull_nr_devs; i++) {
    //         scull_devices[i].quantum = scull_quantum;
    //         scull_devices[i].qset = scull_qset;
    //         sema_init(&scull_devices[i].sem, 1);
    // #ifdef CONFIG_DEVFS_FS
    //         sprintf(devname, "%i", i);
    //         devfs_register(scull_devfs_dir, devname,
    //                        DEVFS_FL_AUTO_DEVNUM,
    //                        0, 0, S_IFCHR | S_IRUGO | S_IWUGO,
    //                        &scull_fops,
    //                        scull_devices+i);
    // #endif  
    //     }

    //     /* At this point call the init function for any friend device */
    //     if ( (result = scull_p_init()) )
    //         goto fail;
    //     if ( (result = scull_access_init()) )
    //         goto fail;
    //     /* ... */

    // #ifndef SCULL_DEBUG
    //     EXPORT_NO_SYMBOLS; /* otherwise, leave global symbols visible */
    // #endif

    // #ifdef SCULL_DEBUG /* only when debugging */
    //     scull_create_proc();
    // #endif
      struct file_operations scull_fops;
        register_chrdev(scull_major, "scull", &scull_fops);
        return 0; /* succeed */

    //   fail:
    //     scull_cleanup_module();
    //     return result;
    }



    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("DYYR");

  • 相关阅读:
    语句的输入和输出 数据类型 运算符
    第一章 进制转换
    1. JDK 、 JRE 、JVM有什么区别和联系?
    office toolkit怎么用(以激活office professional 2013为例)
    PHP代码审计4-漏洞挖掘思路
    PHP代码审计3-SQL注入,CSRF,动态函数执行与匿名函数执行,unserialize 反序列化漏洞,变量覆盖,文件管理,文件上传
    PHP代码审计2-常用超全局变量,常用命令注入,常用XSS漏洞审计,文件包含
    PHP代码审计1-审计环境与调试函数
    小白日记54:kali渗透测试之Web渗透-补充概念(AJAX,WEB Service)
    小白日记53:kali渗透测试之Web渗透-SSL、TLS中间人攻击(SSLsplit,Mitmproxy,SSLstrip),拒绝服务攻击
  • 原文地址:https://www.cnblogs.com/yiru/p/2794658.html
Copyright © 2011-2022 走看看