Commit 402d3265 authored by David Howells's avatar David Howells Committed by David Woodhouse

NOMMU: Present backing device capabilities for MTD chardevs

Present backing device capabilities for MTD character device files to allow
NOMMU mmap to do direct mapping where possible.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Tested-by: default avatarBernd Schmidt <bernd.schmidt@analog.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 9ce96908
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Core functionality. # Core functionality.
obj-$(CONFIG_MTD) += mtd.o obj-$(CONFIG_MTD) += mtd.o
mtd-y := mtdcore.o mtdsuper.o mtd-y := mtdcore.o mtdsuper.o mtdbdi.o
mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
......
...@@ -21,6 +21,8 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch ...@@ -21,6 +21,8 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
static int mapram_erase (struct mtd_info *, struct erase_info *); static int mapram_erase (struct mtd_info *, struct erase_info *);
static void mapram_nop (struct mtd_info *); static void mapram_nop (struct mtd_info *);
static struct mtd_info *map_ram_probe(struct map_info *map); static struct mtd_info *map_ram_probe(struct map_info *map);
static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long,
unsigned long, unsigned long);
static struct mtd_chip_driver mapram_chipdrv = { static struct mtd_chip_driver mapram_chipdrv = {
...@@ -64,6 +66,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) ...@@ -64,6 +66,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
mtd->type = MTD_RAM; mtd->type = MTD_RAM;
mtd->size = map->size; mtd->size = map->size;
mtd->erase = mapram_erase; mtd->erase = mapram_erase;
mtd->get_unmapped_area = mapram_unmapped_area;
mtd->read = mapram_read; mtd->read = mapram_read;
mtd->write = mapram_write; mtd->write = mapram_write;
mtd->sync = mapram_nop; mtd->sync = mapram_nop;
...@@ -79,6 +82,20 @@ static struct mtd_info *map_ram_probe(struct map_info *map) ...@@ -79,6 +82,20 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
} }
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long mapram_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct map_info *map = mtd->priv;
return (unsigned long) map->virt + offset;
}
static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
......
...@@ -20,6 +20,8 @@ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch ...@@ -20,6 +20,8 @@ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
static void maprom_nop (struct mtd_info *); static void maprom_nop (struct mtd_info *);
static struct mtd_info *map_rom_probe(struct map_info *map); static struct mtd_info *map_rom_probe(struct map_info *map);
static int maprom_erase (struct mtd_info *mtd, struct erase_info *info); static int maprom_erase (struct mtd_info *mtd, struct erase_info *info);
static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long,
unsigned long, unsigned long);
static struct mtd_chip_driver maprom_chipdrv = { static struct mtd_chip_driver maprom_chipdrv = {
.probe = map_rom_probe, .probe = map_rom_probe,
...@@ -40,6 +42,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map) ...@@ -40,6 +42,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
mtd->name = map->name; mtd->name = map->name;
mtd->type = MTD_ROM; mtd->type = MTD_ROM;
mtd->size = map->size; mtd->size = map->size;
mtd->get_unmapped_area = maprom_unmapped_area;
mtd->read = maprom_read; mtd->read = maprom_read;
mtd->write = maprom_write; mtd->write = maprom_write;
mtd->sync = maprom_nop; mtd->sync = maprom_nop;
...@@ -53,6 +56,20 @@ static struct mtd_info *map_rom_probe(struct map_info *map) ...@@ -53,6 +56,20 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
} }
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long maprom_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct map_info *map = mtd->priv;
return (unsigned long) map->virt + offset;
}
static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
......
...@@ -65,6 +65,19 @@ static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) ...@@ -65,6 +65,19 @@ static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{ {
} }
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
return (unsigned long) mtd->priv + offset;
}
static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
...@@ -116,6 +129,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, ...@@ -116,6 +129,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
mtd->erase = ram_erase; mtd->erase = ram_erase;
mtd->point = ram_point; mtd->point = ram_point;
mtd->unpoint = ram_unpoint; mtd->unpoint = ram_unpoint;
mtd->get_unmapped_area = ram_get_unmapped_area;
mtd->read = ram_read; mtd->read = ram_read;
mtd->write = ram_write; mtd->write = ram_write;
......
/* Internal MTD definitions
*
* Copyright © 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/*
* mtdbdi.c
*/
extern struct backing_dev_info mtd_bdi_unmappable;
extern struct backing_dev_info mtd_bdi_ro_mappable;
extern struct backing_dev_info mtd_bdi_rw_mappable;
/* MTD backing device capabilities
*
* Copyright © 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h>
#include "internal.h"
/*
* backing device capabilities for non-mappable devices (such as NAND flash)
* - permits private mappings, copies are taken of the data
*/
struct backing_dev_info mtd_bdi_unmappable = {
.capabilities = BDI_CAP_MAP_COPY,
};
/*
* backing device capabilities for R/O mappable devices (such as ROM)
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
struct backing_dev_info mtd_bdi_ro_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP),
};
/*
* backing device capabilities for writable mappable devices (such as RAM)
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
struct backing_dev_info mtd_bdi_rw_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
BDI_CAP_WRITE_MAP),
};
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h> #include <linux/mtd/compatmac.h>
...@@ -107,12 +108,15 @@ static int mtd_open(struct inode *inode, struct file *file) ...@@ -107,12 +108,15 @@ static int mtd_open(struct inode *inode, struct file *file)
goto out; goto out;
} }
if (MTD_ABSENT == mtd->type) { if (mtd->type == MTD_ABSENT) {
put_mtd_device(mtd); put_mtd_device(mtd);
ret = -ENODEV; ret = -ENODEV;
goto out; goto out;
} }
if (mtd->backing_dev_info)
file->f_mapping->backing_dev_info = mtd->backing_dev_info;
/* You can't open it RW if it's not a writeable device */ /* You can't open it RW if it's not a writeable device */
if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
put_mtd_device(mtd); put_mtd_device(mtd);
...@@ -781,6 +785,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -781,6 +785,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return ret; return ret;
} /* memory_ioctl */ } /* memory_ioctl */
/*
* try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
* mappings)
*/
#ifndef CONFIG_MMU
static unsigned long mtd_get_unmapped_area(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
unsigned long flags)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->get_unmapped_area) {
unsigned long offset;
if (addr != 0)
return (unsigned long) -EINVAL;
if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
return (unsigned long) -EINVAL;
offset = pgoff << PAGE_SHIFT;
if (offset > mtd->size - len)
return (unsigned long) -EINVAL;
return mtd->get_unmapped_area(mtd, len, offset, flags);
}
/* can't map directly */
return (unsigned long) -ENOSYS;
}
#endif
/*
* set up a mapping for shared memory segments
*/
static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
{
#ifdef CONFIG_MMU
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM)
return 0;
return -ENOSYS;
#else
return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
#endif
}
static const struct file_operations mtd_fops = { static const struct file_operations mtd_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.llseek = mtd_lseek, .llseek = mtd_lseek,
...@@ -789,6 +846,10 @@ static const struct file_operations mtd_fops = { ...@@ -789,6 +846,10 @@ static const struct file_operations mtd_fops = {
.ioctl = mtd_ioctl, .ioctl = mtd_ioctl,
.open = mtd_open, .open = mtd_open,
.release = mtd_close, .release = mtd_close,
.mmap = mtd_mmap,
#ifndef CONFIG_MMU
.get_unmapped_area = mtd_get_unmapped_area,
#endif
}; };
static int __init init_mtdchar(void) static int __init init_mtdchar(void)
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include "internal.h"
#include "mtdcore.h" #include "mtdcore.h"
...@@ -46,6 +47,20 @@ int add_mtd_device(struct mtd_info *mtd) ...@@ -46,6 +47,20 @@ int add_mtd_device(struct mtd_info *mtd)
{ {
int i; int i;
if (!mtd->backing_dev_info) {
switch (mtd->type) {
case MTD_RAM:
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
break;
case MTD_ROM:
mtd->backing_dev_info = &mtd_bdi_ro_mappable;
break;
default:
mtd->backing_dev_info = &mtd_bdi_unmappable;
break;
}
}
BUG_ON(mtd->writesize == 0); BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex); mutex_lock(&mtd_table_mutex);
......
...@@ -84,6 +84,18 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) ...@@ -84,6 +84,18 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
part->master->unpoint(part->master, from + part->offset, len); part->master->unpoint(part->master, from + part->offset, len);
} }
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct mtd_part *part = PART(mtd);
offset += part->offset;
return part->master->get_unmapped_area(part->master, len, offset,
flags);
}
static int part_read_oob(struct mtd_info *mtd, loff_t from, static int part_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
...@@ -342,6 +354,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, ...@@ -342,6 +354,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.name = part->name; slave->mtd.name = part->name;
slave->mtd.owner = master->owner; slave->mtd.owner = master->owner;
slave->mtd.backing_dev_info = master->backing_dev_info;
slave->mtd.read = part_read; slave->mtd.read = part_read;
slave->mtd.write = part_write; slave->mtd.write = part_write;
...@@ -354,6 +367,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, ...@@ -354,6 +367,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.unpoint = part_unpoint; slave->mtd.unpoint = part_unpoint;
} }
if (master->get_unmapped_area)
slave->mtd.get_unmapped_area = part_get_unmapped_area;
if (master->read_oob) if (master->read_oob)
slave->mtd.read_oob = part_read_oob; slave->mtd.read_oob = part_read_oob;
if (master->write_oob) if (master->write_oob)
......
...@@ -162,6 +162,20 @@ struct mtd_info { ...@@ -162,6 +162,20 @@ struct mtd_info {
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */ /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
/* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags);
/* Backing device capabilities for this device
* - provides mmap capabilities
*/
struct backing_dev_info *backing_dev_info;
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment