码迷,mamicode.com
首页 > 其他好文 > 详细

一个完整的虚拟字符设备驱动程序

时间:2016-06-01 23:07:47      阅读:271      评论:0      收藏:0      [点我收藏+]

标签:

字符驱动模块charmem.c

#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 <linux/uaccess.h>
#include "charmem.h"

#define CHARMEM_SIZE    4096    /*设备内存大小*/
#define CHARMEM_MAJOR    261        /*驱动的主设备号*/


static int mem_major=CHARMEM_MAJOR;        /*设备的主设备号*/
module_param(mem_major,int,S_IRUGO);    /*将主设备号作为参数传入到模块*/

/*自定义的字符驱动存储结构体*/
struct struct_mem_dev
{
    struct cdev cdev;                    /*字符设备驱动结构体(内核)*/
    unsigned char mem[CHARMEM_SIZE];    /*字符设备分配的内存*/
};

struct struct_mem_dev *mem_devp;        /*程序用结构体*/


/*字符驱动打开函数*/
static int mem_open(struct inode *inode,struct file *filp)
{
    filp->private_data=mem_devp;
    return 0;
}


/*字符驱动释放函数*/
static int mem_release(struct inode *inode,struct file *filp)
{
    return 0;
}


/*字符驱动特定指令函数*/
static long mem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
    //struct struct_mem_dev *dev=filp->private_data;
    int result=0,ioarg=0;
    //char * chararg;
    /*检查指令是否合法*/
    if(_IOC_TYPE(cmd)!=MEM_MAGIC)   /*如果指令类型不等于预定义的幻数*/
        return -EINVAL;

    if(_IOC_NR(cmd)>MEM_MAXNR)        /*指令序号最大值*/
        return -EINVAL;

    /*检查权限*/
    if(_IOC_DIR(cmd) & _IOC_READ)
        result=!access_ok(VERIFY_WRITE,(void *)arg,_IOC_SIZE(cmd));
    else if(_IOC_DIR(cmd) & _IOC_WRITE)
        result=!access_ok(VERIFY_READ,(void *)arg,_IOC_SIZE(cmd));
    
    if(result)
        return -EFAULT;


    /*执行指令*/
    switch(cmd)
    {
        case MEM_CLEAR:        /*清理命令*/
            printk(KERN_INFO "Kernel Execute ‘MEM_CLEAR‘ Command!\n");
            break;
        case MEM_GETDATA:    /*获取数据ioarg-->arg*/
            ioarg=2016;
            result=__put_user(ioarg,(int *)arg);
            break;
        case MEM_SETDATA:    /*设置参数arg-->ioarg*/
            result=__get_user(ioarg,(int *)arg);
            printk(KERN_INFO "Kernel Execute ‘MEM_SETDATA‘ Command,ioarg=%d!\n",ioarg);
            break;
//        case MEM_PRINTSTR:
//            result=__get_user(chararg,(char *)arg1);
//            printk(KERN_INFO "Kernel Execute ‘MEM_PRINTSTR‘ Command,chararg=%s\n",chararg);
//            break;
        default:
            return -EINVAL;        /*返回错误*/
    }

    return 0;
}


/*字符驱动读取函数
struct file *filp:    文件指针
char __user *buf:    用户空间内存
size_t size:        读取的数量
loff_t *poss:        文件内指针偏移量
*/
static ssize_t mem_read(struct file *filp,char __user *buf,size_t size,loff_t *poss)
{
    unsigned long p=*poss;        /*文件内指针位置*/
    unsigned int count=size;    /*要读取的数据大小*/
    int result=0;                /*返回值*/
    struct struct_mem_dev *dev=filp->private_data;    /*获取保存在filp中的字符驱动数据*/
    
    if(p>=CHARMEM_SIZE)    /*如果当前指针大于文件内存数*/
        return 0;
    
    if(count>CHARMEM_SIZE-p)        /*要读取的数量大于剩余的数量*/
        count=CHARMEM_SIZE-p;
    
    if(copy_to_user(buf,dev->mem+p,count))
    {
        result=-EINVAL;
    }
    else
    {
        *poss=count+*poss;            /*更新文件内指针位置*/
        result=count;                /*返回读取到的数据数量*/
        printk(KERN_INFO "read [%d] bytes from %lu\n",count,p);
    }
    
    return result;
}


/*字符驱动写入函数*/
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *poss)
{
    unsigned long p=*poss;
    unsigned int count=size;
    int result=0;

    struct struct_mem_dev *dev=filp->private_data;    /*获取保存在filp中的字符驱动数据*/
    if(p>=CHARMEM_SIZE)            /*如果要写入的大小超过了预设的内存大小*/
        return 0;

    if(count>CHARMEM_SIZE-p)    /*如果要写入的大小超过了剩余的内存大小,则删掉剩余额数据*/
        count=CHARMEM_SIZE-p;

    /*将用户空间的数据写入到内核空间中*/
    if(copy_from_user(dev->mem+p,buf,count))
    {
        *poss=count+*poss;
        result=count;
        printk(KERN_INFO "write [%u] bytes from %lu\n",count,p);
    }
    return result;
}


/*文件内指针移动函数*/
static loff_t mem_llseek(struct file *filp,loff_t offset,int orig)
{
    loff_t result=0;

    switch(orig)
    {
        case 0:        /*0表示SEEK_SET,文件开头*/
            if(offset<0)    /*位置错误*/
            {
                result=-EINVAL;
                break;
            }
            if((unsigned int)offset>CHARMEM_SIZE) /*位置超出预设内存范围*/
            {
                result=-EINVAL;
                break;
            }
            filp->f_pos=(unsigned int)offset;
            result=filp->f_pos;
            break;
        case 1:        /*1表示SEEK_CUR,文件当前位置*/
            if((filp->f_pos+offset)<CHARMEM_SIZE)    /*文件指针超出预设内存范围*/
            {
                result=-EINVAL;
                break;
            }
            if((filp->f_pos+offset)<0)    /*文件指针偏移错误*/
            {
                result=-EINVAL;
                break;
            }
            filp->f_pos=filp->f_pos+offset;    /*更新文件指针*/
            result=filp->f_pos;
            break;
        default:        /*默认操作*/
            result=-EINVAL;
            break;
    }
    return result;
}


/*文件操作集*/
static const struct file_operations mem_fops=
{
    .owner        = THIS_MODULE,    /*指向拥有这个模块的指针*/
    .llseek        = mem_llseek,        /*文件内指针偏移操作*/
    .read        = mem_read,        /*字符设备读取*/
    .write        = mem_write,    /*字符设备写入*/
    .open        = mem_open,        /*字符设备打开*/
    .release    = mem_release,    /*字符设备释放*/
    .unlocked_ioctl    = mem_ioctl,    /*向字符设备发出特定指令*/
};



/*字符设备设置函数*/
static void mem_setup_dev(struct struct_mem_dev *dev,int index)
{
    int err,devno;
    devno=MKDEV(mem_major,index);    /*利用MKDEV宏生成字符设备号*/

    /*初始化字符设备*/
    cdev_init(&dev->cdev,&mem_fops);
    dev->cdev.owner=THIS_MODULE;
    err=cdev_add(&dev->cdev,devno,1);

    if(err)
    {
        printk(KERN_INFO "mem_setup_dev ERROR:%d adding cdev[%d].\n",err,index);
    }
}



/*模块初始化*/
static int __init mymem_init(void)
{
    int result;
    dev_t devno=MKDEV(mem_major,0);

    if(mem_major)    /*如果主设备号已经预先设置*/
    {
        result=register_chrdev_region(devno,1,"chardev");    /*静态分配设备号*/
    }
    else
    {
        result=alloc_chrdev_region(&devno,0,1,"chardev");    /*动态分配设备号*/
        mem_major=MAJOR(devno);        /*获取分配到的主设备号*/
    }

    if(result<0)
        return result;

    /*为驱动分配内核物理内存,大小为4K*/
    mem_devp=kzalloc(sizeof(struct struct_mem_dev),GFP_KERNEL);
    if(!mem_devp)
    {
        result=-ENOMEM;
        goto fail_malloc;
    }

    mem_setup_dev(mem_devp,0);
    return 0;

    fail_malloc:
    unregister_chrdev_region(devno,1);    /*销毁之前注册的字符设备*/
    return result;

}

/*模块注销函数*/
static void __exit mymem_exit(void)
{
    cdev_del(&mem_devp->cdev);    /*从系统中删除掉该字符设备*/
    kfree(mem_devp);            /*释放分配的内存*/
    unregister_chrdev_region(MKDEV(mem_major,0),1);    /*销毁之前注册的字符设备*/
}


module_init(mymem_init);
module_exit(mymem_exit);

/*模块声明*/
MODULE_AUTHOR("EDISON REN");
MODULE_LICENSE("GPL");

功能预定义charmem.h

#ifndef _CHARMEM_H
#define _CHARMEM_H

#include <linux/ioctl.h>

/*ioctl指令*/
#define MEM_MAGIC        ‘j‘                                /*幻数*/
#define MEM_CLEAR        _IO(MEM_MAGIC,0x1a)                /*清理指令*/
#define MEM_SETDATA        _IOW(MEM_MAGIC,0x1b,int)        /*设置指令*/
#define MEM_GETDATA        _IOR(MEM_MAGIC,0x1c,int)        /*读取指令*/
//#define MEM_PRINTSTR    _IOW(MEM_MAGIC,0x1d,char*)        /*字符串*/
#define MEM_MAXNR        0x1c                            /*指令序号最大值*/

#endif

应用程序charmemapp.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "charmem.h"


/*字符设备驱动名称*/
#define DEV_PATH        "/dev/chardev"

int main(void)
{
    int fd,result=0,arg;
    //char * chararg="hello world!";
    fd=open(DEV_PATH,O_RDWR);
    
    if(fd<0)
    {
        printf("打开字符设备文件失败!\n");
    }
    else
    {
        printf("打开字符设备文件成功!\n");
        printf("*****************************\n");
        /*执行MEM_CLEAR指令*/
        result=ioctl(fd,MEM_CLEAR);

        if(result<0)
            printf("执行‘MEM_CLEAR‘指令失败!\n");
        else
            printf("执行指令‘MEM_CLEAR‘成功!\n");
        printf("*****************************\n");

        /*执行MEM_GETDATA指令*/
        result=ioctl(fd,MEM_GETDATA,&arg);
        if(result<0)
            printf("执行‘MEM_GETDATA‘指令失败!\n");
        else
            printf("执行‘MEM_GETDATA‘指令成功.读取的数据:%d!\n",arg);

        printf("*****************************\n");

        /*执行MEM_SETDATA指令*/
        arg=2016;
        result=ioctl(fd,MEM_SETDATA,&arg);
        if(result<0)
            printf("执行‘MEM_SETDATA‘指令失败!\n");
        else
            printf("执行‘MEM_SETDATA‘指令成功!\n");

        /*执行MEM_PRINTSTR指令*/
//        result=ioctl(fd,MEM_PRINTSTR,&chararg);
//        if(result<0)
//            printf("执行‘MEM_PRINTSTR‘指令失败!\n");
//        else
//            printf("执行‘MEM_PRINTSTR‘指令成功!\n");
    }
    close(fd);

    return 0;
}

Makefile文件

obj-m := charmem.o

KDIR := /usr/src/linux-headers-3.13.0-32-generic

all:
    make -C $(KDIR) M=$(PWD) modules
clean:
    rm -rf *.ko *.o *.mod.o *.mod.c *.symvers *.mod *.order

 

一个完整的虚拟字符设备驱动程序

标签:

原文地址:http://www.cnblogs.com/foggia2004/p/5551258.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!