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

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

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

原文地址:https://www.cnblogs.com/plinx/p/2867487.html