您的位置:首页 > 其它

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

2016-06-01 22:02 411 查看
字符驱动模块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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: