使用文件私有数据的字符设备驱动设计
1. 编写源代码
/* mem_dev.h */
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 260
//memdev采用静态分配设备号,不要和其它设备重复
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
//mem设备描述结构体
struct mem_dev
{
char *data;
unsigned long size;
};
#endif
/* mem_dev.c */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mem_dev.h"
static int mem_major = MEMDEV_MAJOR;
module_param(mem_major,int,S_IRUGO);
struct mem_dev *mem_devp;
struct cdev cdev;
int mem_open(struct inode *inode,struct file *filp)
{
struct mem_dev *dev;
int num = MINOR(inode->i_rdev);
if(num >=MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
filp->private_data = dev;
return 0;
}
int mem_release(struct inode *inode,struct file *filp)
{
return 0;
}
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 ret = 0;
struct mem_dev *dev = filp->private_data;
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_to_user(buf,(void*)(dev->data+p),count))
{
ret = -EFAULT;
}
else
{
*poss +=count;
ret = count;
printk(KERN_INFO "read %d bytes from %lu/n",count,p);
}
return ret;
}
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 ret=0;
struct mem_dev *dev = filp->private_data;
if(p>=MEMDEV_SIZE)
return 0;
if(count>MEMDEV_SIZE-p)
count = MEMDEV_SIZE-p;
if(copy_from_user(dev->data+p,buf,count))
{
ret = -EFAULT;
}
else
{
*poss += count;
ret = count;
printk(KERN_INFO "write %d bytes from %lu/n",count,p);
}
return ret;
}
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0:
newpos = offset;
break;
case 1:
newpos = filp->f_pos + offset;
break;
case 2:
newpos = MEMDEV_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}
if((newpos<0) || (newpos>MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
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,
};
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
if(mem_major)
result = register_chrdev_region(devno,2,"memdev");
else
{
result = alloc_chrdev_region(&devno,0,2,"memdev");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
cdev_init(&cdev,&mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)
{
result = -ENOMEM;
goto fail_malloc;
}
memset(mem_devp,0,MEMDEV_NR_DEVS * sizeof(struct mem_dev));
for(i=0;i {
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE);
}
return 0;
fail_malloc:
unregister_chrdev_region(devno,2);
return result;
}
static void memdev_exit(void)
{
cdev_del(&cdev);
kfree(mem_devp);
unregister_chrdev_region(MKDEV(mem_major,0),2);
}
MODULE_AUTHOR("Zechin Liao");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
/* Makefile */
KVERS = $(shell uname -r)
# Kernel modules
obj-m += mem_dev.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
/* memdev_app.c */
#include
#include
#include
#include
#include
int main()
{
int fd;
char buf[4096];
printf("\n");
strcpy(buf,"This is a example of charactar devices driver");
printf("buf:%s\n",buf);
fd=open("/dev/memdev",O_RDWR);
if(fd == -1)
{
printf("open mem_dev failed!\n");
return -1;
}
write(fd,buf,sizeof(buf));
lseek(fd,0,SEEK_SET);
strcpy(buf,"nothing");
read(fd,buf,sizeof(buf));
printf("buf:%s\n",buf);
return 0;
}
2. 编译
输入“make”命令编译,得到mem_dev.ko文件。
3. 加载模块
输入“sudo insmod mem_dev.ko”,通过“lsmod”命令,发现mem_dev模块已加载。再通过“cat /proc/devices”命令查看,发现多出了主设备号为260的“mem_dev”字符设备驱动。
4. 创建设备节点
输入“sudo mknod /dev/memdev c 260 0”创建设备节点。
5. 验证
通过“sudo echo "hello world" > /dev/memdev”命令和“sudo cat /dev/memdev”命令分别验证设备的写和读,结果证明字符串正确的写入了mem_dev字符设备。
6. 应用程序验证
(1)编译memdev_app.c
gcc -o memdev_app memdev_app.c
(2)运行memdev_app
./memdev_app
7. 删除设备节点
sudo rm /dev/memdev
8. 卸载模块
rmmod mem_dev
至此,虚拟字符设备驱动完满完成。
阅读(476) | 评论(0) | 转发(0) |