zoukankan      html  css  js  c++  java
  • 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

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

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

  • 相关阅读:
    Leetcode 92. Reverse Linked List II
    Leetcode 206. Reverse Linked List
    Leetcode 763. Partition Labels
    Leetcode 746. Min Cost Climbing Stairs
    Leetcode 759. Employee Free Time
    Leetcode 763. Partition Labels
    搭建数据仓库第09篇:物理建模
    Python进阶篇:Socket多线程
    Python进阶篇:文件系统的操作
    搭建数据仓库第08篇:逻辑建模–5–维度建模核心之一致性维度2
  • 原文地址:https://www.cnblogs.com/plinx/p/2867487.html
Copyright © 2011-2022 走看看