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