1 2 3 4#include <linux/module.h> 5#include <asm/uaccess.h> 6#include <linux/types.h> 7#include <linux/kernel.h> 8#include <linux/ptrace.h> 9#include <linux/slab.h> 10#include <linux/string.h> 11#include <linux/timer.h> 12#include <linux/major.h> 13#include <linux/fs.h> 14#include <linux/ioctl.h> 15#include <linux/init.h> 16#include <asm/io.h> 17#include <asm/system.h> 18 19#include <linux/mtd/mtd.h> 20 21#define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ 22#define SLRAM_BLK_SZ 0x4000 23 24#define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) 25#define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) 26 27typedef struct slram_priv { 28 u_char *start; 29 u_char *end; 30} slram_priv_t; 31 32typedef struct slram_mtd_list { 33 struct mtd_info *mtdinfo; 34 struct slram_mtd_list *next; 35} slram_mtd_list_t; 36 37#ifdef MODULE 38static char *map[SLRAM_MAX_DEVICES_PARAMS]; 39 40module_param_array(map, charp, NULL, 0); 41MODULE_PARM_DESC(map, "List of memory regions to map. \"map=<name>, <start>, <length / end>\""); 42#else 43static char *map; 44#endif 45 46static slram_mtd_list_t *slram_mtdlist = NULL; 47 48static int slram_erase(struct mtd_info *, struct erase_info *); 49static int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); 50static void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); 51static int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); 52static int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 53 54static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) 55{ 56 slram_priv_t *priv = mtd->priv; 57 58 if (instr->addr + instr->len > mtd->size) { 59 return(-EINVAL); 60 } 61 62 memset(priv->start + instr->addr, 0xff, instr->len); 63 64 /* This'll catch a few races. Free the thing before returning :) 65 * I don't feel at all ashamed. This kind of thing is possible anyway 66 * with flash, but unlikely. 67 */ 68 69 instr->state = MTD_ERASE_DONE; 70 71 mtd_erase_callback(instr); 72 73 return(0); 74} 75 76static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, 77 size_t *retlen, u_char **mtdbuf) 78{ 79 slram_priv_t *priv = mtd->priv; 80 81 if (from + len > mtd->size) 82 return -EINVAL; 83 84 *mtdbuf = priv->start + from; 85 *retlen = len; 86 return(0); 87} 88 89static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) 90{ 91} 92 93static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, 94 size_t *retlen, u_char *buf) 95{ 96 slram_priv_t *priv = mtd->priv; 97 98 if (from > mtd->size) 99 return -EINVAL; 100 101 if (from + len > mtd->size) 102 len = mtd->size - from; 103 104 memcpy(buf, priv->start + from, len); 105 106 *retlen = len; 107 return(0); 108} 109 110static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, 111 size_t *retlen, const u_char *buf) 112{ 113 slram_priv_t *priv = mtd->priv; 114 115 if (to + len > mtd->size) 116 return -EINVAL; 117 118 memcpy(priv->start + to, buf, len); 119 120 *retlen = len; 121 return(0); 122} 123 124/*====================================================================*/ 125 126static int register_device(char *name, unsigned long start, unsigned long length) 127{ 128 slram_mtd_list_t **curmtd; 129 130 curmtd = &slram_mtdlist; 131 while (*curmtd) { 132 curmtd = &(*curmtd)->next; 133 } 134 135 *curmtd = kmalloc(sizeof(slram_mtd_list_t), GFP_KERNEL); 136 if (!(*curmtd)) { 137 E("slram: Cannot allocate new MTD device.\n"); 138 return(-ENOMEM); 139 } 140 (*curmtd)->mtdinfo = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); 141 (*curmtd)->next = NULL; 142 143 if ((*curmtd)->mtdinfo) { 144 (*curmtd)->mtdinfo->priv = 145 kzalloc(sizeof(slram_priv_t), GFP_KERNEL); 146 147 if (!(*curmtd)->mtdinfo->priv) { 148 kfree((*curmtd)->mtdinfo); 149 (*curmtd)->mtdinfo = NULL; 150 } 151 } 152 153 if (!(*curmtd)->mtdinfo) { 154 E("slram: Cannot allocate new MTD device.\n"); 155 return(-ENOMEM); 156 } 157 158 if (!(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start = 159 ioremap(start, length))) { 160 E("slram: ioremap failed\n"); 161 return -EIO; 162 } 163 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end = 164 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start + length; 165 166 167 (*curmtd)->mtdinfo->name = name; 168 (*curmtd)->mtdinfo->size = length; 169 (*curmtd)->mtdinfo->flags = MTD_CAP_RAM; 170 (*curmtd)->mtdinfo->erase = slram_erase; 171 (*curmtd)->mtdinfo->point = slram_point; 172 (*curmtd)->mtdinfo->unpoint = slram_unpoint; 173 (*curmtd)->mtdinfo->read = slram_read; 174 (*curmtd)->mtdinfo->write = slram_write; 175 (*curmtd)->mtdinfo->owner = THIS_MODULE; 176 (*curmtd)->mtdinfo->type = MTD_RAM; 177 (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; 178 (*curmtd)->mtdinfo->writesize = 1; 179 180 if (add_mtd_device((*curmtd)->mtdinfo)) { 181 E("slram: Failed to register new device\n"); 182 iounmap(((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start); 183 kfree((*curmtd)->mtdinfo->priv); 184 kfree((*curmtd)->mtdinfo); 185 return(-EAGAIN); 186 } 187 T("slram: Registered device %s from %luKiB to %luKiB\n", name, 188 (start / 1024), ((start + length) / 1024)); 189 T("slram: Mapped from 0x%p to 0x%p\n", 190 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->start, 191 ((slram_priv_t *)(*curmtd)->mtdinfo->priv)->end); 192 return(0); 193} 194 195static void unregister_devices(void) 196{ 197 slram_mtd_list_t *nextitem; 198 199 while (slram_mtdlist) { 200 nextitem = slram_mtdlist->next; 201 del_mtd_device(slram_mtdlist->mtdinfo); 202 iounmap(((slram_priv_t *)slram_mtdlist->mtdinfo->priv)->start); 203 kfree(slram_mtdlist->mtdinfo->priv); 204 kfree(slram_mtdlist->mtdinfo); 205 kfree(slram_mtdlist); 206 slram_mtdlist = nextitem; 207 } 208} 209 210static unsigned long handle_unit(unsigned long value, char *unit) 211{ 212 if ((*unit == 'M') || (*unit == 'm')) { 213 return(value * 1024 * 1024); 214 } else if ((*unit == 'K') || (*unit == 'k')) { 215 return(value * 1024); 216 } 217 return(value); 218} 219 220static int parse_cmdline(char *devname, char *szstart, char *szlength) 221{ 222 char *buffer; 223 unsigned long devstart; 224 unsigned long devlength; 225 226 if ((!devname) || (!szstart) || (!szlength)) { 227 unregister_devices(); 228 return(-EINVAL); 229 } 230 231 devstart = simple_strtoul(szstart, &buffer, 0); 232 devstart = handle_unit(devstart, buffer); 233 234 if (*(szlength) != '+') { 235 devlength = simple_strtoul(szlength, &buffer, 0); 236 devlength = handle_unit(devlength, buffer) - devstart; 237 } else { 238 devlength = simple_strtoul(szlength + 1, &buffer, 0); 239 devlength = handle_unit(devlength, buffer); 240 } 241 T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", 242 devname, devstart, devlength); 243 if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { 244 E("slram: Illegal start / length parameter.\n"); 245 return(-EINVAL); 246 } 247 248 if ((devstart = register_device(devname, devstart, devlength))){ 249 unregister_devices(); 250 return((int)devstart); 251 } 252 return(0); 253} 254 255#ifndef MODULE 256 257static int __init mtd_slram_setup(char *str) 258{ 259 map = str; 260 return(1); 261} 262 263__setup("slram=", mtd_slram_setup); 264 265#endif 266 267static int init_slram(void) 268{ 269 char *devname; 270 int i; 271 272#ifndef MODULE 273 char *devstart; 274 char *devlength; 275 276 i = 0; 277 278 if (!map) { 279 E("slram: not enough parameters.\n"); 280 return(-EINVAL); 281 } 282 while (map) { 283 devname = devstart = devlength = NULL; 284 285 if (!(devname = strsep(&map, ","))) { 286 E("slram: No devicename specified.\n"); 287 break; 288 } 289 T("slram: devname = %s\n", devname); 290 if ((!map) || (!(devstart = strsep(&map, ",")))) { 291 E("slram: No devicestart specified.\n"); 292 } 293 T("slram: devstart = %s\n", devstart); 294 if ((!map) || (!(devlength = strsep(&map, ",")))) { 295 E("slram: No devicelength / -end specified.\n"); 296 } 297 T("slram: devlength = %s\n", devlength); 298 if (parse_cmdline(devname, devstart, devlength) != 0) { 299 return(-EINVAL); 300 } 301 } 302#else 303 int count; 304 305 for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS); 306 count++) { 307 } 308 309 if ((count % 3 != 0) || (count == 0)) { 310 E("slram: not enough parameters.\n"); 311 return(-EINVAL); 312 } 313 for (i = 0; i < (count / 3); i++) { 314 devname = map[i * 3]; 315 316 if (parse_cmdline(devname, map[i * 3 + 1], map[i * 3 + 2])!=0) { 317 return(-EINVAL); 318 } 319 320 } 321#endif /* !MODULE */ 322 323 return(0); 324} 325 326static void __exit cleanup_slram(void) 327{ 328 unregister_devices(); 329} 330 331module_init(init_slram); 332module_exit(cleanup_slram); 333 334MODULE_LICENSE("GPL"); 335MODULE_AUTHOR("Jochen Schaeuble <psionic@psionic.de>"); 336MODULE_DESCRIPTION("MTD driver for uncached system RAM"); 337