mtd/drivers/mtd/devices ramtd.c, NONE, 1.1 Kconfig, 1.16, 1.17 Makefile.common, 1.7, 1.8

joern at infradead.org joern at infradead.org
Fri May 13 05:45:52 EDT 2005


Update of /home/cvs/mtd/drivers/mtd/devices
In directory phoenix.infradead.org:/home/joern/mtd/drivers/mtd/devices

Modified Files:
	Kconfig Makefile.common 
Added Files:
	ramtd.c 
Log Message:
Add ramtd driver.
This driver is similar to mtdram, but allocates/frees memory
dynamically, thus being able to support devices much larger than
physical memory.



--- NEW FILE ramtd.c ---
/**
 * Simple mtd driver that dynamically allocates/frees memory
 *
 * $Id: ramtd.c,v 1.1 2005/05/13 09:45:48 joern Exp $
 *
 * Copyright (c) 2005 Joern Engel <joern at wh.fh-wedel.de>
 */
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/vmalloc.h>

struct ramtd {
	struct mtd_info mtd;
	struct list_head list;
	u32 size;
	void *page[];
};

static LIST_HEAD(ramtd_list);
static DECLARE_MUTEX(ramtd_mutex);


static void *get_pool_page(void)
{
	TRACE();
	void *ret = (void*)__get_free_page(GFP_KERNEL);
	return ret;
}


static void free_pool_page(void *page)
{
	TRACE();
	free_page((unsigned long)page);
}


static int ramtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	struct ramtd *ramtd = container_of(mtd, typeof(*ramtd), mtd);
	u32 addr = instr->addr;
	u32 len = instr->len;

	TRACE();
	if (addr + len > mtd->size)
		return -EINVAL;
	if (addr % PAGE_SIZE)
		return -EINVAL;
	if (len % PAGE_SIZE)
		return -EINVAL;

	while (len) {
		u32 page = addr / PAGE_SIZE;

		down_interruptible(&ramtd_mutex);
		kfree(ramtd->page[page]);
		ramtd->page[page] = NULL;
		up(&ramtd_mutex);

		addr += PAGE_SIZE;
		len -= PAGE_SIZE;
	}
	instr->state = MTD_ERASE_DONE;
	mtd_erase_callback(instr);

	return 0;
}


static int ramtd_read(struct mtd_info *mtd, loff_t from, size_t len,
		size_t *retlen, u_char *buf)
{
	struct ramtd *ramtd = container_of(mtd, typeof(*ramtd), mtd);

	TRACE();
	if (from >= mtd->size)
		return -EINVAL;

	if (len > mtd->size - from)
		len = mtd->size - from;

	while (len) {
		u32 page = from / PAGE_SIZE;
		u32 ofs = from % PAGE_SIZE;
		u32 rlen = min_t(u32, len, PAGE_SIZE - ofs);

		down_interruptible(&ramtd_mutex);
		if (!ramtd->page[page])
			memset(buf, 0xff, rlen);
		else
			memcpy(buf, ramtd->page[page] + ofs, rlen);
		up(&ramtd_mutex);

		buf += rlen;
		from += rlen;
		*retlen += rlen;
		len -= rlen;
	}
	return 0;
}


static int ramtd_write(struct mtd_info *mtd, loff_t to, size_t len,
		size_t *retlen, const u_char *buf)
{
	struct ramtd *ramtd = container_of(mtd, typeof(*ramtd), mtd);

	TRACE();
	if (to >= mtd->size)
		return -EINVAL;

	if (len > mtd->size - to)
		len = mtd->size - to;

	*retlen = 0;

	while (len) {
		u32 page = to / PAGE_SIZE;
		u32 ofs = to % PAGE_SIZE;
		u32 wlen = min_t(u32, len, PAGE_SIZE - ofs);

		down_interruptible(&ramtd_mutex);
		if (!ramtd->page[page]) {
			TRACE();
			ramtd->page[page] = get_pool_page;
			memset(ramtd->page[page], 0xff, PAGE_SIZE);
		}
		if (!ramtd->page[page])
			return -EIO;

		TRACE();
		memcpy(ramtd->page[page] + ofs, buf, wlen);
		up(&ramtd_mutex);

		buf += wlen;
		to += wlen;
		*retlen += wlen;
		len -= wlen;
	}
	return 0;
}


static int register_device(const char *name, u32 size)
{
	struct ramtd *new;
	u32 pages_size;

	TRACE();
	size = PAGE_ALIGN(size);
	pages_size = size / PAGE_SIZE * sizeof(void*);
	new = vmalloc(sizeof(*new) + pages_size);
	if (!new)
		return -ENOMEM;
	memset(new, 0, sizeof(*new) + pages_size);

	new->mtd.name = (char*)name;
	new->mtd.size = size;
	new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
	new->mtd.owner = THIS_MODULE;
	new->mtd.type = MTD_RAM;
	new->mtd.erasesize = PAGE_SIZE;
	new->mtd.write = ramtd_write;
	new->mtd.read = ramtd_read;
	new->mtd.erase = ramtd_erase;

	if (add_mtd_device(&new->mtd)) {
		free_pool_page(new);
		return -EAGAIN;
	}

	down_interruptible(&ramtd_mutex);
	list_add(&new->list, &ramtd_list);
	up(&ramtd_mutex);
	return 0;
}


static int __init ramtd_init(void)
{
	TRACE();
	return register_device("ramtd", 4*1024*1024); /* FIXME */
}

static void __exit ramtd_exit(void)
{
	struct ramtd *this, *next;

	TRACE();
	down_interruptible(&ramtd_mutex);
	list_for_each_entry_safe(this, next, &ramtd_list, list) {
		del_mtd_device(&this->mtd);
		kfree(this);
	}
	up(&ramtd_mutex);
}


module_init(ramtd_init);
module_exit(ramtd_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Joern Engel <joern at wh.fh-wedel.de>");
MODULE_DESCRIPTION("MTD using dynamic memory allocation");

Index: Kconfig
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/devices/Kconfig,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- Kconfig	6 Jan 2005 15:15:47 -0000	1.16
+++ Kconfig	13 May 2005 09:45:48 -0000	1.17
@@ -113,6 +113,15 @@
 	  allocating space from Linux's available memory. Otherwise, leave 
 	  this set to zero. Most people will want to leave this as zero.
 
+config RAMTD
+	tristate "MTD using dynamic memory allocation"
+	depends on MTD
+	help
+	  This driver dynamically allocates memory as the devices are written
+	  to and frees it on erases.
+
+	  If unsure, say n.
+
 config MTD_BLKMTD
 	tristate "MTD emulation using block device"
 	depends on MTD

Index: Makefile.common
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/devices/Makefile.common,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- Makefile.common	22 Dec 2004 17:51:15 -0000	1.7
+++ Makefile.common	13 May 2005 09:45:48 -0000	1.8
@@ -23,3 +23,4 @@
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLKMTD)	+= blkmtd.o
 obj-$(CONFIG_MTD_BLOCK2MTD)	+= block2mtd.o
+obj-$(CONFIG_RAMTD)		+= ramtd.o





More information about the linux-mtd-cvs mailing list