《内核kernel:mmap内存映射模块编写》

一、模块编写

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/kfifo.h>

#define DEMO_NAME "my_demo_dev"
static struct device *mydemodrv_device;

/*virtual FIFO device's buffer*/
static char *device_buffer;
#define MAX_DEVICE_BUFFER_SIZE (10 * PAGE_SIZE)

#define MYDEV_CMD_GET_BUFSIZE 1	/* defines our IOCTL cmd */

static int demodrv_open(struct inode *inode, struct file *file)
{
	int major = MAJOR(inode->i_rdev);
	int minor = MINOR(inode->i_rdev);

	printk("%s: major=%d, minor=%d\n", __func__, major, minor);

	return 0;
}

static int demodrv_release(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t
demodrv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int nbytes = 
		simple_read_from_buffer(buf, count, ppos, device_buffer, MAX_DEVICE_BUFFER_SIZE);

	printk("%s: read nbytes=%d done at pos=%d\n",
		 __func__, nbytes, (int)*ppos);

	return nbytes;
}

static ssize_t
demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	int nbytes = 
		simple_write_to_buffer(device_buffer, MAX_DEVICE_BUFFER_SIZE, ppos, buf, count);

	printk("%s: write nbytes=%d done at pos=%d\n",
		 __func__, nbytes, (int)*ppos);

	return nbytes;
}

static int 
demodrv_mmap(struct file *filp, struct vm_area_struct *vma)
{
	unsigned long pfn;
	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
	unsigned long len = vma->vm_end - vma->vm_start;

	if (offset >= MAX_DEVICE_BUFFER_SIZE)
		return -EINVAL;
	if (len > (MAX_DEVICE_BUFFER_SIZE - offset))
		return -EINVAL;

	printk("%s: mapping %ld bytes of device buffer at offset %ld\n",
		 __func__, len, offset);

	/*    pfn = page_to_pfn (virt_to_page (ramdisk + offset)); */
	pfn = virt_to_phys(device_buffer + offset) >> PAGE_SHIFT;

	if (remap_pfn_range(vma, vma->vm_start, pfn, len, vma->vm_page_prot))
		return -EAGAIN;

	return 0;
}

static long
demodrv_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	unsigned long tbs = MAX_DEVICE_BUFFER_SIZE;
	void __user *ioargp = (void __user *)arg;

	switch (cmd) {
	default:
		return -EINVAL;

	case MYDEV_CMD_GET_BUFSIZE:
		if (copy_to_user(ioargp, &tbs, sizeof(tbs)))
			return -EFAULT;
		return 0;
	}
}

static const struct file_operations demodrv_fops = {
	.owner = THIS_MODULE,
	.open = demodrv_open,
	.release = demodrv_release,
	.read = demodrv_read,
	.write = demodrv_write,
	.mmap = demodrv_mmap,
	.unlocked_ioctl = demodrv_unlocked_ioctl,
};

static struct miscdevice mydemodrv_misc_device = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEMO_NAME,
	.fops = &demodrv_fops,
};

static int __init simple_char_init(void)
{
	int ret;

	device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
	if (!device_buffer)
		return -ENOMEM;

	ret = misc_register(&mydemodrv_misc_device);
	if (ret) {
		printk("failed register misc device\n");
		kfree(device_buffer);
		return ret;
	}

	mydemodrv_device = mydemodrv_misc_device.this_device;

	printk("succeeded register char device: %s\n", DEMO_NAME);

	return 0;
}

static void __exit simple_char_exit(void)
{
	printk("removing device\n");

	kfree(device_buffer);
	misc_deregister(&mydemodrv_misc_device);
}

module_init(simple_char_init);
module_exit(simple_char_exit);

MODULE_AUTHOR("HarkerYX");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("simpe character device");

 

二、Makefile

BASEINCLUDE ?= /home/yexiang/work/running_kernel/runninglinuxkernel_4.0
#BASEINCLUDE ?= /lib/modules/`uname -r`/build

mydevdemo_mmap-objs := mydev_mmap.o

obj-m   :=   mydevdemo_mmap.o
all : 
        $(MAKE) -C $(BASEINCLUDE) M=$(PWDp modules;

clean:
        $(MAKE) -C $(BASEINCLUDE) SUBDIRS=$(PWD) clean;
        rm -f *.ko;

编译: make

运行:

/ # insmod /mnt/mydevdemo_mmap.ko 
[19716.273507] succeeded register char device: my_demo_dev

 

三、测试程序

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <malloc.h>

#define DEMO_DEV_NAME "/dev/my_demo_dev"

#define MYDEV_CMD_GET_BUFSIZE 1	/* defines our IOCTL cmd */

int main()
{
	int fd;
	int i;
	size_t len;
	char message[] = "Testing the virtual FIFO device";
	char *read_buffer, *mmap_buffer;

	len = sizeof(message);

	fd = open(DEMO_DEV_NAME, O_RDWR);
	if (fd < 0) {
		printf("open device %s failded\n", DEMO_DEV_NAME);
		return -1;
	}

	if (ioctl(fd, MYDEV_CMD_GET_BUFSIZE, &len) < 0) {
		printf("ioctl fail\n");
		goto open_fail;
	}

	printf("driver max buffer size=%d\n", len);

	read_buffer = malloc(len);
	if (!read_buffer)
		goto open_fail;

	mmap_buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (mmap_buffer == (char *)MAP_FAILED) {
		printf("mmap driver buffer fail\n");
		goto map_fail;
	}

	printf("mmap driver buffer succeeded: %p\n", mmap_buffer);

	/* modify the mmaped buffer */
	for (i = 0; i < len; i++)
		*(mmap_buffer + i) = (char)random();

	/* read the buffer back and compare with the mmap buffer*/
	if (read(fd, read_buffer, len) != len) {
		printf("read fail\n");
		goto read_fail;
	}

	if (memcmp(read_buffer, mmap_buffer, len)) {
		printf("buffer compare fail\n");
		goto read_fail;
	}

	printf("data modify and compare succussful\n");

	munmap(mmap_buffer, len);
	free(read_buffer);
	close(fd);

	return 0;

read_fail:
	munmap(mmap_buffer, len);
map_fail:
	free(read_buffer);
open_fail:
	close(fd);
	return -1;

}

编译:arm-linux-gnueabi-gcc mmap_test.c -o mmap_test --static

运行:

/ # /mnt/mmap_test 
[19725.936111] demodrv_open: major=10, minor=58
driver max buffer size=40960
[19725.970907] demodrv_mmap: mapping 40960 bytes of device buffer at offset 0
mmap driver buffer succeeded: 0xb6fad000
[19726.002538] demodrv_read: read nbytes=40960 done at pos=40960
data modify and compare succussful

 

上一篇:git使用方法1


下一篇:用mmap做I/O