Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * mtdram - a test mtd device
0003  * Author: Alexander Larsson <alex@cendio.se>
0004  *
0005  * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
0006  * Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de>
0007  *
0008  * This code is GPL
0009  *
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include <linux/ioport.h>
0015 #include <linux/vmalloc.h>
0016 #include <linux/mm.h>
0017 #include <linux/init.h>
0018 #include <linux/mtd/mtd.h>
0019 #include <linux/mtd/mtdram.h>
0020 
0021 static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
0022 static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE;
0023 static unsigned long writebuf_size = 64;
0024 #define MTDRAM_TOTAL_SIZE (total_size * 1024)
0025 #define MTDRAM_ERASE_SIZE (erase_size * 1024)
0026 
0027 module_param(total_size, ulong, 0);
0028 MODULE_PARM_DESC(total_size, "Total device size in KiB");
0029 module_param(erase_size, ulong, 0);
0030 MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
0031 module_param(writebuf_size, ulong, 0);
0032 MODULE_PARM_DESC(writebuf_size, "Device write buf size in Bytes (Default: 64)");
0033 
0034 // We could store these in the mtd structure, but we only support 1 device..
0035 static struct mtd_info *mtd_info;
0036 
0037 static int check_offs_len(struct mtd_info *mtd, loff_t ofs, uint64_t len)
0038 {
0039     int ret = 0;
0040 
0041     /* Start address must align on block boundary */
0042     if (mtd_mod_by_eb(ofs, mtd)) {
0043         pr_debug("%s: unaligned address\n", __func__);
0044         ret = -EINVAL;
0045     }
0046 
0047     /* Length must align on block boundary */
0048     if (mtd_mod_by_eb(len, mtd)) {
0049         pr_debug("%s: length not block aligned\n", __func__);
0050         ret = -EINVAL;
0051     }
0052 
0053     return ret;
0054 }
0055 
0056 static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
0057 {
0058     if (check_offs_len(mtd, instr->addr, instr->len))
0059         return -EINVAL;
0060     memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
0061 
0062     return 0;
0063 }
0064 
0065 static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
0066         size_t *retlen, void **virt, resource_size_t *phys)
0067 {
0068     *virt = mtd->priv + from;
0069     *retlen = len;
0070 
0071     if (phys) {
0072         /* limit retlen to the number of contiguous physical pages */
0073         unsigned long page_ofs = offset_in_page(*virt);
0074         void *addr = *virt - page_ofs;
0075         unsigned long pfn1, pfn0 = vmalloc_to_pfn(addr);
0076 
0077         *phys = __pfn_to_phys(pfn0) + page_ofs;
0078         len += page_ofs;
0079         while (len > PAGE_SIZE) {
0080             len -= PAGE_SIZE;
0081             addr += PAGE_SIZE;
0082             pfn0++;
0083             pfn1 = vmalloc_to_pfn(addr);
0084             if (pfn1 != pfn0) {
0085                 *retlen = addr - *virt;
0086                 break;
0087             }
0088         }
0089     }
0090 
0091     return 0;
0092 }
0093 
0094 static int ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
0095 {
0096     return 0;
0097 }
0098 
0099 static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
0100         size_t *retlen, u_char *buf)
0101 {
0102     memcpy(buf, mtd->priv + from, len);
0103     *retlen = len;
0104     return 0;
0105 }
0106 
0107 static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
0108         size_t *retlen, const u_char *buf)
0109 {
0110     memcpy((char *)mtd->priv + to, buf, len);
0111     *retlen = len;
0112     return 0;
0113 }
0114 
0115 static void __exit cleanup_mtdram(void)
0116 {
0117     if (mtd_info) {
0118         mtd_device_unregister(mtd_info);
0119         vfree(mtd_info->priv);
0120         kfree(mtd_info);
0121     }
0122 }
0123 
0124 int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
0125         unsigned long size, const char *name)
0126 {
0127     memset(mtd, 0, sizeof(*mtd));
0128 
0129     /* Setup the MTD structure */
0130     mtd->name = name;
0131     mtd->type = MTD_RAM;
0132     mtd->flags = MTD_CAP_RAM;
0133     mtd->size = size;
0134     mtd->writesize = 1;
0135     mtd->writebufsize = writebuf_size;
0136     mtd->erasesize = MTDRAM_ERASE_SIZE;
0137     mtd->priv = mapped_address;
0138 
0139     mtd->owner = THIS_MODULE;
0140     mtd->_erase = ram_erase;
0141     mtd->_point = ram_point;
0142     mtd->_unpoint = ram_unpoint;
0143     mtd->_read = ram_read;
0144     mtd->_write = ram_write;
0145 
0146     if (mtd_device_register(mtd, NULL, 0))
0147         return -EIO;
0148 
0149     return 0;
0150 }
0151 
0152 static int __init init_mtdram(void)
0153 {
0154     void *addr;
0155     int err;
0156 
0157     if (!total_size)
0158         return -EINVAL;
0159 
0160     /* Allocate some memory */
0161     mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
0162     if (!mtd_info)
0163         return -ENOMEM;
0164 
0165     addr = vmalloc(MTDRAM_TOTAL_SIZE);
0166     if (!addr) {
0167         kfree(mtd_info);
0168         mtd_info = NULL;
0169         return -ENOMEM;
0170     }
0171     err = mtdram_init_device(mtd_info, addr, MTDRAM_TOTAL_SIZE, "mtdram test device");
0172     if (err) {
0173         vfree(addr);
0174         kfree(mtd_info);
0175         mtd_info = NULL;
0176         return err;
0177     }
0178     memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
0179     return err;
0180 }
0181 
0182 module_init(init_mtdram);
0183 module_exit(cleanup_mtdram);
0184 
0185 MODULE_LICENSE("GPL");
0186 MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>");
0187 MODULE_DESCRIPTION("Simulated MTD driver for testing");