• 设备驱动之一 基于第三章的组织结构改变的scull驱动



           LDD3到第三章的组织结构改变的scull驱动

    scull.h文件---驱动头文件

    /**********************************************
     * Author: lewiyon@hotmail.com
     * File name: scull.h
     * Description: define scull
     * Date: 2012-07-4
     *********************************************/
    #ifndef __SCULL_H
    #define __SCULL_H
    
    #include <linux/semaphore.h>
    #include <linux/cdev.h>
    
    #ifndef SCULL_MAJOR
    #define SCULL_MAJOR     0
    #endif
    
    #ifndef SCULL_NR_DEVS
    #define SCULL_NR_DEVS   1
    #endif
    
    #ifndef SCULL_QUANTUM
    #define SCULL_QUANTUM   4096
    #endif
    
    #ifndef SCULL_QSET
    #define SCULL_QSET      4096
    #endif
    
    /*
     * parameters of module
     */
    extern int scull_major;
    extern int scull_quantum;
    extern int scull_qset;
    
    struct scull_qset {
        void **data;
        struct scull_qset *next;
    };
    
    /*
     * define scull_dev struct
     */
    struct scull_dev {
        struct scull_qset *data; /* point first child set */
        int quantum;             /* size of current set   */
        int qset;                /* size of array         */
        unsigned long size;      /* save total quantity   */
        unsigned int access_key; /* used by scullid and scullpriv */
        struct semaphore sem;    /* mutex                 */
        struct cdev cdev;        /* character device      */
    };
    
    extern int scull_trim(struct scull_dev *dev);
    
    #endif

    main.c -- 驱动模块实现源码

    /**********************************************
     * Author: lewiyon@hotmail.com
     * File name: scullmod.c 
     * Description: initialize and release function for scull
     * Date: 2012-07-4
     *********************************************/
    
    #include <linux/init.h>         /* module        */
    #include <linux/module.h>       /* module        */
    #include <linux/moduleparam.h>  /* module        */
    #include <linux/errno.h>        /* error codes   */
    #include <linux/kernel.h>       /* printk        */
    #include <linux/slab.h>         /* kmalloc kfree */
    #include <linux/types.h>        /* dev_t         */
    
    /* local head files */
    #include "scull.h"
    #include "file.h"
    
    /* default parameters of module */
    int scull_major = SCULL_MAJOR;
    int scull_minor = 0;
    int scull_nr_devs = SCULL_NR_DEVS;
    int scull_quantum = SCULL_QUANTUM;
    int scull_qset = SCULL_QSET;
    
    /* input parameters of module */
    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);
    
    struct scull_dev *scull_devices;
    
    /*
     * scull_trim -  遍历链表,并释放所有找到的量子和量子集
     * @dev: scull设备
     */
    int scull_trim(struct scull_dev *dev)
    {
        int i, qset;
        struct scull_qset *next, *dptr;
        
        qset = dev->qset;
        for (dptr = dev->data; dptr; dptr = next) {
            if (dptr->data) {
                for (i = 0; i < qset; i++)
                    kfree(dptr->data[i]);
                kfree(dptr->data);
                dptr->data = NULL;
            }
            next = dptr->next;
            kfree(dptr);
        }
        dev->size = 0;
        dev->quantum = scull_quantum;
        dev->qset = scull_qset;
        dev->data = NULL;
    
        return 0;
    }
    
    static void scull_setup_cdev(struct scull_dev *dev, int index)
    {
        int err;
        int devno;
    
        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);
        
    }
    
    /*
     * initialze scull module 
     */
    void scull_cleanup_module(void)
    {
        int i;
        dev_t devno;
    
        devno = MKDEV(scull_major, scull_minor);
        /* delete each entry */
        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);
        }
    
        /* unregister */
        unregister_chrdev_region(devno, scull_nr_devs);
        printk(KERN_WARNING "scull: Bye!\n");
    }
    
    
    /*
     * initialze scull module 
     */
    int scull_init_module(void)
    {
        int i, res;
        dev_t dev = 0;
        
        if (scull_major) {
            dev = MKDEV(scull_major, scull_minor);
            res = register_chrdev_region(dev, scull_nr_devs, "scull");
        } else {
            res = alloc_chrdev_region(&dev, scull_minor, 
                    scull_nr_devs, "scull");
            scull_major = MAJOR(dev);
        }
        if (res < 0) {
            printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
            return res;
        }
    
        /* 
         * allocate the device struct cache
         */
        scull_devices = kmalloc(scull_nr_devs * sizeof(struct scull_dev), 
                GFP_KERNEL);
        if (NULL == scull_devices) {
            res = -ENOMEM;
            printk(KERN_WARNING "scull: NOMEM for scull!\n");
            goto fail;
        }
        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;
            scull_devices[i].qset = scull_qset;
            sema_init(&scull_devices[i].sem, 1);
            scull_setup_cdev(&scull_devices[i], i);
        }
        printk(KERN_INFO "scull: OK!\n");
        return 0;
    fail:
        scull_cleanup_module();
        return res;
    }
    
    module_init(scull_init_module);
    module_exit(scull_cleanup_module);
    
    MODULE_AUTHOR("lewiyon@hotmail.com");
    MODULE_LICENSE("GPL");
    

    file.h  -- 文件操作函数头文件

    /**********************************************
     * Author: lewiyon@hotmail.com
     * File name: file.h
     * Description: file header 
     * Date: 2012-07-4
     *********************************************/
    #ifndef __SCULL_FILE_H
    #define __SCULL_FILE_H
    
    extern const struct file_operations scull_fops;
    
    #endif

    file.c -- 文件操作函数源码

    /**********************************************
     * Author: lewiyon@hotmail.com
     * File name: file.c
     * Description: realize cull file ops
     * Date: 2012-07-04
     *********************************************/
    
    #include <linux/module.h>       /* THIS_MODULE          */
    #include <linux/kernel.h>       /* printk & container   */
    #include <linux/uaccess.h>      /* cp_to/from_user      */
    #include <linux/types.h>        /* size_t               */
    #include <linux/fs.h>           /* inode st.            */
    
    #include "scull.h" 
    
    /* 
     * scull_follow - 在指定的scull_dev中查找指定的量子集
     * @dev: scull_dev设备结构体指针
     * @n: 量子集在scull_dev中的位置
     * 
     * return: 
     *        @对应量子集结构指针 - 成功;
     *        @NULL - 失败;
     */
    struct scull_qset *scull_follow(struct scull_dev *dev, int n)
    {
        struct scull_qset *qs;
        
        qs = dev->data;
        if (!qs)
        {
            qs = dev->data = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
            if (qs == NULL) 
            {
                printk(KERN_WARNING "scull_follow_if_fail\n");
                return NULL;
            }
            memset(qs, 0, sizeof(struct scull_qset));
        }
        
        /* 创建其他的量子集 */
        while (n--) 
        {
            if (!qs->next)
            {
                qs->next = kmalloc(sizeof(struct scull_qset), GFP_KERNEL);
                if (NULL == qs->next)
                {
                    printk(KERN_WARNING "scull_follow_n_%d\n", n);
                    return NULL;
                }
                memset(qs->next, 0, sizeof(struct scull_qset));
            }
            qs = qs->next;
        }
        
        return qs;
    }
    
    /* 
     * scull_read - 从scull_dev中的文件读取数据
     */
    ssize_t scull_read(struct file *filp, char __user *buf, 
            size_t count, loff_t *f_pos)
    {
        struct scull_dev *dev;
        struct scull_qset *dptr;
        int quantum, qset, itemsize;
        int item, rest, s_pos, q_pos;
        ssize_t retval;
        
        dev = filp->private_data;
        quantum = dev->quantum;
        qset = dev->qset;
        itemsize = quantum * qset;
        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;
        
        /* 查找listitem中量子集的索引以及量子偏移位 */
        item = (long)*f_pos / itemsize;    
        rest = (long)*f_pos % itemsize;
        s_pos = rest / quantum;
        q_pos = rest % quantum;
        
        /* 在dev中查找量子集item */
        dptr = scull_follow(dev, item);
        if (NULL == dptr || !dptr->data || !dptr->data[s_pos])
            goto out;
    
        /* 只读取到当前量子的结尾 */        
        if (count > quantum - q_pos)
            count = quantum - q_pos;
        
        if (copy_to_user(buf, dptr->data[s_pos] + q_pos, count))
        {
            retval = -EFAULT;
            goto out;
        }
        *f_pos += count;
        retval = count;
        
    out:
        up(&dev->sem);
        return retval;
    }
    
    /* 
     * scull_write - 往scull_dev中的文件写入数据
     */
    ssize_t scull_write(struct file *filp, const char __user *buf, 
            size_t count, loff_t *f_pos)
    {
        struct scull_dev *dev;
        struct scull_qset *dptr;
        int quantum , qset;
        int itemsize, item, s_pos, q_pos, rest;
        ssize_t retval;    
        
        dev = filp->private_data;
        quantum = dev->quantum;
        qset = dev->qset;
        itemsize = quantum * qset;
        retval = -ENOMEM;
        
        if (down_interruptible(&dev->sem))
            return -ERESTARTSYS;
        
        /* 查找listitem中量子集的索引以及量子偏移位 */
        item = (long)*f_pos / itemsize;    
        rest = (long)*f_pos % itemsize;
        s_pos = rest / quantum;
        q_pos = rest % quantum;
        
        /* 在dev中查找量子集item */
        dptr = scull_follow(dev, item);
        if (dptr == NULL)
        {
            printk(KERN_WARNING "scull_follow_fail\n");
            goto out;
        }
        if (!dptr->data)
        {
            dptr->data = kmalloc(qset * sizeof(char *), GFP_KERNEL);
            if (!dptr->data)
            {
                printk(KERN_WARNING "km_dptr->data_fail\n");
                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])
            {
                printk(KERN_WARNING "km_dptr->data[s_pos]_fail\n");
                goto out;
            }
        }
        
        /* 只写到当前量子末尾 */
        if (count > quantum - q_pos)
            count = quantum - q_pos;
        
        if (copy_from_user(dptr->data[s_pos] + q_pos, buf, count))
        {
            retval = -EFAULT;
            printk(KERN_WARNING "copy_fail\n");
            goto out;
        }
        *f_pos += count;
        retval = count;
        
        /* 更新scull_dev数据大小记录 */
        if (dev->size < *f_pos)
            dev->size = *f_pos;
    
    out:    
        up(&dev->sem);
        return retval;
    }
    
    /* 
     * scull_llseek - 往scull_dev中的文件写入数据
     */
    loff_t scull_llseek(struct file *filp, loff_t off, int whence)
    {
        struct scull_dev *dev;
        loff_t newpos;
        
        dev = filp->private_data;
        switch(whence) 
        {
            case SEEK_SET:
                newpos = off;
                break;
            case SEEK_CUR:
                newpos = filp->f_pos + off;
                break;
            case SEEK_END:
                newpos = dev->size + off;
                break;
            default:
                return -EINVAL;
        }
        if (newpos < 0) 
            return -EINVAL;
        filp->f_pos = newpos;
        return newpos;
    }
    
    /* 
     * scull_open - 打开scull_dev中的文件
     */
    int scull_open(struct inode *inode, struct file *filp)
    {
        struct scull_dev *dev;
        
        dev = container_of(inode->i_cdev, struct scull_dev, cdev); 
        filp->private_data = dev;
        if (O_WRONLY == (filp->f_flags & O_ACCMODE))
        { 
            if (down_interruptible(&dev->sem))
                return -ERESTARTSYS;
            
            scull_trim(dev); 
            up(&dev->sem);
        }
    
        return 0;
    }
    
    /* 
     * scull_release - 关闭scull_dev中的文件
     */
    int scull_release(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    
    const 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,
    };
    

    makefile文件

    obj-m += scull.o 
    scull-objs := main.o file.o 
    KERNELBUILD := /lib/modules/$(shell uname -r)/build
    default:
    	$(MAKE) -C $(KERNELBUILD) M=$(shell pwd) modules
    clean:
    	-rm -rf *.o .*.cmd *.ko* *.mod.c .tmp_versions
    	-rm -rf  modules.order Module.symvers
    
    

    测试文件:

    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include  <errno.h>
    
    
    int main()
    {
        int fp0;
        char Buf[4096];
        
        /* 初始化Buf */
        strcpy(Buf,"Scull is char dev!");
        printf("BUF: %s\n",Buf);
        
        /* 打开设备文件 */
        fp0 = open("/dev/scull0", O_RDWR);
        if (fp0 < 0)
        {
            printf("Open scull Error!\n");
            return -1;
        }
        
        /* 写入设备 */
        strcpy(Buf,"Scull write!");
        write(fp0, Buf, sizeof(Buf));
        /* 重新定位文件位置 */
        lseek(fp0,0,SEEK_SET);
        
        /* 清除Buf */
        strcpy(Buf,"Buf is NULL!");
        
        /* 读出设备 */
        read(fp0, Buf, sizeof(Buf));
        /* 检测结果 */
        printf("BUF: %s success\n",Buf);
        
        return 0;    
    
    }


    参考文献:(部分代码摘自下列网站)

          http://www.cnblogs.com/adolph-suyu/archive/2011/12/04/2275990.html

  • 相关阅读:
    ELF文件格式解析
    了解ELF文件类型
    汇编程序-简介
    汇编指令入门级整理
    双系统: Windows10时间快和Ubuntu时间差问题
    iDRAC on DELL Server
    ubuntu WIFI 连接不稳定(wifi无法连接问题)
    删除字符串中的空格(空字符)
    VBS 基础知识
    VBS 操作Excel的常用方法
  • 原文地址:https://www.cnblogs.com/youngerchina/p/5624617.html
Copyright © 2020-2023  润新知