• CTDIY1字符设备驱动的使用


      CTDIY means Copy to DIY.

      首先来copy一个例程来试试字符设备到底是如何

    //globalmem.c 本例程来源于《linux设备驱动开发详解》
    #include<linux/module.h>
    #include<linux/types.h>
    #include<linux/fs.h>
    #include<linux/errno.h>
    #include<linux/mm.h>
    #include<linux/sched.h>
    #include<linux/init.h>
    #include<linux/cdev.h>
    #include<linux/slab.h>
    #include<asm/io.h>
    #include<asm/system.h>
    #include<asm/uaccess.h>
    
    #define GLOBALMEM_SIZE     0x1000            //Max memory
    #define MEM_CLEAR    0x1            //clear all the memory
    #define    GLOBALMEM_MAJOR    250            //the major
    
    static int globalmem_major = GLOBALMEM_MAJOR;
    /*globalmem 设备结构体*/
    struct globalmem_dev{
        struct    cdev    cdev;            //cdev struct
        unsigned char    mem[GLOBALMEM_SIZE];    //global memory
    };
    
    struct globalmem_dev *globalmem_devp;        //cdev pointer
    //file open operation
    int globalmem_open(struct inode *inode, struct file *filp)
    {
        filp->private_data = globalmem_devp;
        return 0;
    }
    //file release operation
    int globalmem_release(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    //file ioctl operation
    static int globalmem_ioctl(struct inode *inodep, struct file *filp, 
                    unsigned int cmd, unsigned long arg)
    {
        struct globalmem_dev *dev = filp->private_data;    //get the cdev struct pointer
        
        switch(cmd){
        case MEM_CLEAR:
            memset(dev->mem, 0, GLOBALMEM_SIZE);
            printk(KERN_INFO "globalmem is set to zero\n");
            break;
    
        default:
            return - EINVAL;
        }
        return 0;
    }
    //cdev read operation
    static ssize_t globalmem_read(struct file *filp, char __user *buf, 
                    size_t size, loff_t *ppos)
    {
        unsigned long p = *ppos;
        unsigned int count = size;
        int ret = 0;
        struct globalmem_dev *dev = filp->private_data;
    
        //get the real length
        if(p >= GLOBALMEM_SIZE)
            return 0;
        if(count > GLOBALMEM_SIZE - p)
            count = GLOBALMEM_SIZE - p;
    
        //kernel space to the usr space
        if(copy_to_user(buf, (void *)(dev->mem + p),count)){
            ret = - EFAULT;
        }else{
            *ppos += count;
            ret = count;
        
            printk(KERN_INFO "read %u byte(s) form %lu \n", count, p);
        }
    
        return ret;
    }
    
    //cdev write operation
    static ssize_t globalmem_write(struct file *filp, const char __user *buf,
                    size_t size, loff_t *ppos)
    {
        unsigned long p = *ppos;
        unsigned int count = size;
        int ret = 0;
        struct globalmem_dev *dev = filp->private_data;    //get the cdev struct pointer
    
        //get the real length
        if(p >= GLOBALMEM_SIZE)
            return 0;
        if(count > GLOBALMEM_SIZE - p)
            count = GLOBALMEM_SIZE - p;
    
        //kernel space to the usr space
        if(copy_to_user((dev->mem + p), buf, count)){
            ret = - EFAULT;
        }else{
            *ppos += count;
            ret = count;
        
            printk(KERN_INFO "written %u byte(s) form %lu \n", count, p);
        }
    
        return ret;
    }
    
    //cdev seek operation
    static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
    {
        loff_t ret = 0;
        switch(orig){
        case 0:
            if(offset < 0){
                ret = - EINVAL;
                break;
            }
            if((unsigned int)offset > GLOBALMEM_SIZE){
                ret = - EINVAL;
                break;
            }
            filp->f_pos = (unsigned int)offset;
            ret = filp->f_pos;
            break;
    
        case 1:
            if((filp->f_pos + offset) > GLOBALMEM_SIZE){
                ret = - EINVAL;
                break;
            }
            if((filp->f_pos + offset) < 0){
                ret = - EINVAL;
                break;
            }
            filp->f_pos += offset;
            ret = filp->f_pos;
            break;
    
        default:
            ret = - EINVAL;
            break;
        }
        return ret;
    }
    
    
    //the file control struct
    static const struct file_operations globalmem_fops = {
        .owner = THIS_MODULE,
        .llseek = globalmem_llseek,
        .read = globalmem_read,
        .write = globalmem_write,
        .unlocked_ioctl = globalmem_ioctl,
        .open = globalmem_open,
        .release = globalmem_release,
    };
    
    //setup the cdev
    static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
    {
        int err, devno = MKDEV(globalmem_major, index);
    
        cdev_init(&dev->cdev, &globalmem_fops);
        dev->cdev.owner = THIS_MODULE;
        err = cdev_add(&dev->cdev, devno, 1);
        if(err)
            printk(KERN_NOTICE "Error %d adding globalmem %d", err, index);
    }
    
    //module init operation
    int globalmem_init(void)
    {
        int result;
        dev_t devno = MKDEV(globalmem_major,0);
    
        if(globalmem_major)
            result = register_chrdev_region(devno, 1, "globalmem");
        else{
            result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
            globalmem_major = MAJOR(devno);
        }
        if (result < 0)
            return result;
    
        globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
        if (!globalmem_devp){
            result = - ENOMEM;
            goto fail_malloc;
        }
    
        memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
    
        globalmem_setup_cdev(globalmem_devp, 0);
        return 0;
    
    fail_malloc:
        unregister_chrdev_region(devno, 1);
        return result;
    
    }
    
    //module remove operation
    void globalmem_exit(void)
    {
        cdev_del(&globalmem_devp->cdev);
        kfree(globalmem_devp);
        unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
    }
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    module_param(globalmem_major, int, S_IRUGO);
    
    module_init(globalmem_init);
    module_exit(globalmem_exit);
        

      此版本在ubuntu12.04上运行通过,适用于3.2.0版本的内核

      相较于书本上的例程有两处修改

      1、在file_operations中ioctl类型的修改

    //2.6版本
    struct file_operations {
        ……
        int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
        ……
    };
    //3.2版本
    struct file_operations {
        ……
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        ……
    };
    //故在make的时候将报错: 初始值设定项里有未知的字段‘ioctl’
    //只需将struct file_operations中对ioctl的调用 .ioctl 改为 .locked_ioctl即可

      2、版本更换导致的头文件移位

    //3.2中将报错提示缺少一下两个文件
    /home/lufee/mydiraver//globalmem.c:193:2: 错误:隐式声明函数‘kmalloc’ [-Werror=implicit-function-declaration]
    /home/lufee/mydiraver//globalmem.c:210:2: 错误:隐式声明函数‘kfree’ [-Werror=implicit-function-declaration]
    //在原版中中无误,而3.2中将他们放入了linux/slab.h头文件中
    //加入#inclue <linux/slab.h>即可

      Makefile也贴一下吧

    KVERS = $(shell uname -r)
    
    # Kernel modules
    obj-m += globalmem.o
    
    # Specify flags for the module compilation
    #EXTRA_CFLAGS=-g -O0
    
    build:kernel_modules
    
    kernel_modules:
        make    -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
    
    clean:
        make     -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
    
    obj-m := globalmem.o
    modulename-objs := globalmem.o

      通过make之后,产生.ko文件,随后对该字符设备进行如下测试

    # insmod globalmem.ko                            //加载模块
    # lsmod | grep globalmem                         //模块已经加载好了
    globalmem              12827  0 
    # echo "hello world" > globalmem                //写入hello world
    # cat globalmem                                 //读出globalmem内容
    hello world

      至此,一个最简单的字符设备驱动程序完成了。

      接着,开始一点点地剖析其中的道理。

  • 相关阅读:
    C语言 unsigned 和 signed 类型相互转换深入理解
    C++的关键字 explicit的作用———菜鸟级日记
    solr笔记
    矩阵解
    USACO题目——Transformations
    模块化加载时断点调试没反应,进入不了断点
    flash中的渐变滤镜GradientGlowFilter
    Flash Builder 找不到所需的Adobe Flash Player调试器版本的解决办法
    使用Pixel Bender 和Shader Job来处理普通数据运算
    关于Event.ADDED_TO_STAGE
  • 原文地址:https://www.cnblogs.com/plinx/p/2867487.html
Copyright © 2020-2023  润新知