CTDIY-1-字符设备驱动的使用

  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

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

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

 

你可能感兴趣的:(DI)