linux-bk/drivers/mtd/devices/phram.c
<<
>>
Prefs
   1/**
   2 * $Id: phram.c,v 1.11 2005/01/05 18:05:13 dwmw2 Exp $
   3 *
   4 * Copyright (c) ????           Jochen Schäuble <psionic@psionic.de>
   5 * Copyright (c) 2003-2004      Jörn Engel <joern@wh.fh-wedel.de>
   6 *
   7 * Usage:
   8 *
   9 * one commend line parameter per device, each in the form:
  10 *   phram=<name>,<start>,<len>
  11 * <name> may be up to 63 characters.
  12 * <start> and <len> can be octal, decimal or hexadecimal.  If followed
  13 * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or
  14 * gigabytes.
  15 *
  16 * Example:
  17 *      phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
  18 *
  19 */
  20
  21#include <asm/io.h>
  22#include <linux/init.h>
  23#include <linux/kernel.h>
  24#include <linux/list.h>
  25#include <linux/module.h>
  26#include <linux/moduleparam.h>
  27#include <linux/mtd/mtd.h>
  28
  29#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
  30
  31struct phram_mtd_list {
  32        struct mtd_info mtd;
  33        struct list_head list;
  34};
  35
  36static LIST_HEAD(phram_list);
  37
  38
  39
  40static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
  41{
  42        u_char *start = mtd->priv;
  43
  44        if (instr->addr + instr->len > mtd->size)
  45                return -EINVAL;
  46        
  47        memset(start + instr->addr, 0xff, instr->len);
  48
  49        /* This'll catch a few races. Free the thing before returning :) 
  50         * I don't feel at all ashamed. This kind of thing is possible anyway
  51         * with flash, but unlikely.
  52         */
  53
  54        instr->state = MTD_ERASE_DONE;
  55
  56        mtd_erase_callback(instr);
  57
  58        return 0;
  59}
  60
  61static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
  62                size_t *retlen, u_char **mtdbuf)
  63{
  64        u_char *start = mtd->priv;
  65
  66        if (from + len > mtd->size)
  67                return -EINVAL;
  68        
  69        *mtdbuf = start + from;
  70        *retlen = len;
  71        return 0;
  72}
  73
  74static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
  75{
  76}
  77
  78static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
  79                size_t *retlen, u_char *buf)
  80{
  81        u_char *start = mtd->priv;
  82
  83        if (from + len > mtd->size)
  84                return -EINVAL;
  85        
  86        memcpy(buf, start + from, len);
  87
  88        *retlen = len;
  89        return 0;
  90}
  91
  92static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
  93                size_t *retlen, const u_char *buf)
  94{
  95        u_char *start = mtd->priv;
  96
  97        if (to + len > mtd->size)
  98                return -EINVAL;
  99        
 100        memcpy(start + to, buf, len);
 101
 102        *retlen = len;
 103        return 0;
 104}
 105
 106
 107
 108static void unregister_devices(void)
 109{
 110        struct phram_mtd_list *this;
 111
 112        list_for_each_entry(this, &phram_list, list) {
 113                del_mtd_device(&this->mtd);
 114                iounmap(this->mtd.priv);
 115                kfree(this);
 116        }
 117}
 118
 119static int register_device(char *name, unsigned long start, unsigned long len)
 120{
 121        struct phram_mtd_list *new;
 122        int ret = -ENOMEM;
 123
 124        new = kmalloc(sizeof(*new), GFP_KERNEL);
 125        if (!new)
 126                goto out0;
 127
 128        memset(new, 0, sizeof(*new));
 129
 130        ret = -EIO;
 131        new->mtd.priv = ioremap(start, len);
 132        if (!new->mtd.priv) {
 133                ERROR("ioremap failed\n");
 134                goto out1;
 135        }
 136
 137
 138        new->mtd.name = name;
 139        new->mtd.size = len;
 140        new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
 141        new->mtd.erase = phram_erase;
 142        new->mtd.point = phram_point;
 143        new->mtd.unpoint = phram_unpoint;
 144        new->mtd.read = phram_read;
 145        new->mtd.write = phram_write;
 146        new->mtd.owner = THIS_MODULE;
 147        new->mtd.type = MTD_RAM;
 148        new->mtd.erasesize = 0;
 149
 150        ret = -EAGAIN;
 151        if (add_mtd_device(&new->mtd)) {
 152                ERROR("Failed to register new device\n");
 153                goto out2;
 154        }
 155
 156        list_add_tail(&new->list, &phram_list);
 157        return 0;       
 158
 159out2:
 160        iounmap(new->mtd.priv);
 161out1:
 162        kfree(new);
 163out0:
 164        return ret;
 165}
 166
 167static int ustrtoul(const char *cp, char **endp, unsigned int base)
 168{
 169        unsigned long result = simple_strtoul(cp, endp, base);
 170
 171        switch (**endp) {
 172        case 'G':
 173                result *= 1024;
 174        case 'M':
 175                result *= 1024;
 176        case 'k':
 177                result *= 1024;
 178        /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
 179                if ((*endp)[1] == 'i')
 180                        (*endp) += 2;
 181        }
 182        return result;
 183}
 184
 185static int parse_num32(uint32_t *num32, const char *token)
 186{
 187        char *endp;
 188        unsigned long n;
 189
 190        n = ustrtoul(token, &endp, 0);
 191        if (*endp)
 192                return -EINVAL;
 193
 194        *num32 = n;
 195        return 0;
 196}
 197
 198static int parse_name(char **pname, const char *token)
 199{
 200        size_t len;
 201        char *name;
 202
 203        len = strlen(token) + 1;
 204        if (len > 64)
 205                return -ENOSPC;
 206
 207        name = kmalloc(len, GFP_KERNEL);
 208        if (!name)
 209                return -ENOMEM;
 210
 211        strcpy(name, token);
 212
 213        *pname = name;
 214        return 0;
 215}
 216
 217#define parse_err(fmt, args...) do {    \
 218        ERROR(fmt , ## args);   \
 219        return 0;               \
 220} while (0)
 221
 222static int phram_setup(const char *val, struct kernel_param *kp)
 223{
 224        char buf[64+12+12], *str = buf;
 225        char *token[3];
 226        char *name;
 227        uint32_t start;
 228        uint32_t len;
 229        int i, ret;
 230
 231        if (strnlen(val, sizeof(buf)) >= sizeof(buf))
 232                parse_err("parameter too long\n");
 233
 234        strcpy(str, val);
 235
 236        for (i=0; i<3; i++)
 237                token[i] = strsep(&str, ",");
 238
 239        if (str)
 240                parse_err("too many arguments\n");
 241
 242        if (!token[2])
 243                parse_err("not enough arguments\n");
 244
 245        ret = parse_name(&name, token[0]);
 246        if (ret == -ENOMEM)
 247                parse_err("out of memory\n");
 248        if (ret == -ENOSPC)
 249                parse_err("name too long\n");
 250        if (ret)
 251                return 0;
 252
 253        ret = parse_num32(&start, token[1]);
 254        if (ret)
 255                parse_err("illegal start address\n");
 256
 257        ret = parse_num32(&len, token[2]);
 258        if (ret)
 259                parse_err("illegal device length\n");
 260
 261        register_device(name, start, len);
 262
 263        return 0;
 264}
 265
 266module_param_call(phram, phram_setup, NULL, NULL, 000);
 267MODULE_PARM_DESC(phram,"Memory region to map. \"map=<name>,<start>,<length>\"");
 268
 269
 270static int __init init_phram(void)
 271{
 272        return 0;
 273}
 274
 275static void __exit cleanup_phram(void)
 276{
 277        unregister_devices();
 278}
 279
 280module_init(init_phram);
 281module_exit(cleanup_phram);
 282
 283MODULE_LICENSE("GPL");
 284MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
 285MODULE_DESCRIPTION("MTD driver for physical RAM");
 286
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.