ia64/linux-2.6.18-xen.hg

annotate drivers/mtd/mtdchar.c @ 893:f994bfe9b93b

linux/blktap2: reduce TLB flush scope

c/s 885 added very coarse TLB flushing. Since these flushes always
follow single page updates, single page flushes (when available) are
sufficient.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu Jun 04 10:32:57 2009 +0100 (2009-06-04)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $
ian@0 3 *
ian@0 4 * Character-device access to raw MTD devices.
ian@0 5 *
ian@0 6 */
ian@0 7
ian@0 8 #include <linux/device.h>
ian@0 9 #include <linux/fs.h>
ian@0 10 #include <linux/init.h>
ian@0 11 #include <linux/kernel.h>
ian@0 12 #include <linux/module.h>
ian@0 13 #include <linux/slab.h>
ian@0 14 #include <linux/sched.h>
ian@0 15
ian@0 16 #include <linux/mtd/mtd.h>
ian@0 17 #include <linux/mtd/compatmac.h>
ian@0 18
ian@0 19 #include <asm/uaccess.h>
ian@0 20
ian@0 21 static struct class *mtd_class;
ian@0 22
ian@0 23 static void mtd_notify_add(struct mtd_info* mtd)
ian@0 24 {
ian@0 25 if (!mtd)
ian@0 26 return;
ian@0 27
ian@0 28 class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
ian@0 29 NULL, "mtd%d", mtd->index);
ian@0 30
ian@0 31 class_device_create(mtd_class, NULL,
ian@0 32 MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
ian@0 33 NULL, "mtd%dro", mtd->index);
ian@0 34 }
ian@0 35
ian@0 36 static void mtd_notify_remove(struct mtd_info* mtd)
ian@0 37 {
ian@0 38 if (!mtd)
ian@0 39 return;
ian@0 40
ian@0 41 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
ian@0 42 class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
ian@0 43 }
ian@0 44
ian@0 45 static struct mtd_notifier notifier = {
ian@0 46 .add = mtd_notify_add,
ian@0 47 .remove = mtd_notify_remove,
ian@0 48 };
ian@0 49
ian@0 50 /*
ian@0 51 * Data structure to hold the pointer to the mtd device as well
ian@0 52 * as mode information ofr various use cases.
ian@0 53 */
ian@0 54 struct mtd_file_info {
ian@0 55 struct mtd_info *mtd;
ian@0 56 enum mtd_file_modes mode;
ian@0 57 };
ian@0 58
ian@0 59 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
ian@0 60 {
ian@0 61 struct mtd_file_info *mfi = file->private_data;
ian@0 62 struct mtd_info *mtd = mfi->mtd;
ian@0 63
ian@0 64 switch (orig) {
ian@0 65 case SEEK_SET:
ian@0 66 break;
ian@0 67 case SEEK_CUR:
ian@0 68 offset += file->f_pos;
ian@0 69 break;
ian@0 70 case SEEK_END:
ian@0 71 offset += mtd->size;
ian@0 72 break;
ian@0 73 default:
ian@0 74 return -EINVAL;
ian@0 75 }
ian@0 76
ian@0 77 if (offset >= 0 && offset <= mtd->size)
ian@0 78 return file->f_pos = offset;
ian@0 79
ian@0 80 return -EINVAL;
ian@0 81 }
ian@0 82
ian@0 83
ian@0 84
ian@0 85 static int mtd_open(struct inode *inode, struct file *file)
ian@0 86 {
ian@0 87 int minor = iminor(inode);
ian@0 88 int devnum = minor >> 1;
ian@0 89 struct mtd_info *mtd;
ian@0 90 struct mtd_file_info *mfi;
ian@0 91
ian@0 92 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
ian@0 93
ian@0 94 if (devnum >= MAX_MTD_DEVICES)
ian@0 95 return -ENODEV;
ian@0 96
ian@0 97 /* You can't open the RO devices RW */
ian@0 98 if ((file->f_mode & 2) && (minor & 1))
ian@0 99 return -EACCES;
ian@0 100
ian@0 101 mtd = get_mtd_device(NULL, devnum);
ian@0 102
ian@0 103 if (!mtd)
ian@0 104 return -ENODEV;
ian@0 105
ian@0 106 if (MTD_ABSENT == mtd->type) {
ian@0 107 put_mtd_device(mtd);
ian@0 108 return -ENODEV;
ian@0 109 }
ian@0 110
ian@0 111 /* You can't open it RW if it's not a writeable device */
ian@0 112 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
ian@0 113 put_mtd_device(mtd);
ian@0 114 return -EACCES;
ian@0 115 }
ian@0 116
ian@0 117 mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
ian@0 118 if (!mfi) {
ian@0 119 put_mtd_device(mtd);
ian@0 120 return -ENOMEM;
ian@0 121 }
ian@0 122 mfi->mtd = mtd;
ian@0 123 file->private_data = mfi;
ian@0 124
ian@0 125 return 0;
ian@0 126 } /* mtd_open */
ian@0 127
ian@0 128 /*====================================================================*/
ian@0 129
ian@0 130 static int mtd_close(struct inode *inode, struct file *file)
ian@0 131 {
ian@0 132 struct mtd_file_info *mfi = file->private_data;
ian@0 133 struct mtd_info *mtd = mfi->mtd;
ian@0 134
ian@0 135 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
ian@0 136
ian@0 137 if (mtd->sync)
ian@0 138 mtd->sync(mtd);
ian@0 139
ian@0 140 put_mtd_device(mtd);
ian@0 141 file->private_data = NULL;
ian@0 142 kfree(mfi);
ian@0 143
ian@0 144 return 0;
ian@0 145 } /* mtd_close */
ian@0 146
ian@0 147 /* FIXME: This _really_ needs to die. In 2.5, we should lock the
ian@0 148 userspace buffer down and use it directly with readv/writev.
ian@0 149 */
ian@0 150 #define MAX_KMALLOC_SIZE 0x20000
ian@0 151
ian@0 152 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
ian@0 153 {
ian@0 154 struct mtd_file_info *mfi = file->private_data;
ian@0 155 struct mtd_info *mtd = mfi->mtd;
ian@0 156 size_t retlen=0;
ian@0 157 size_t total_retlen=0;
ian@0 158 int ret=0;
ian@0 159 int len;
ian@0 160 char *kbuf;
ian@0 161
ian@0 162 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
ian@0 163
ian@0 164 if (*ppos + count > mtd->size)
ian@0 165 count = mtd->size - *ppos;
ian@0 166
ian@0 167 if (!count)
ian@0 168 return 0;
ian@0 169
ian@0 170 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
ian@0 171 and pass them directly to the MTD functions */
ian@0 172
ian@0 173 if (count > MAX_KMALLOC_SIZE)
ian@0 174 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
ian@0 175 else
ian@0 176 kbuf=kmalloc(count, GFP_KERNEL);
ian@0 177
ian@0 178 if (!kbuf)
ian@0 179 return -ENOMEM;
ian@0 180
ian@0 181 while (count) {
ian@0 182
ian@0 183 if (count > MAX_KMALLOC_SIZE)
ian@0 184 len = MAX_KMALLOC_SIZE;
ian@0 185 else
ian@0 186 len = count;
ian@0 187
ian@0 188 switch (mfi->mode) {
ian@0 189 case MTD_MODE_OTP_FACTORY:
ian@0 190 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 191 break;
ian@0 192 case MTD_MODE_OTP_USER:
ian@0 193 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 194 break;
ian@0 195 case MTD_MODE_RAW:
ian@0 196 {
ian@0 197 struct mtd_oob_ops ops;
ian@0 198
ian@0 199 ops.mode = MTD_OOB_RAW;
ian@0 200 ops.datbuf = kbuf;
ian@0 201 ops.oobbuf = NULL;
ian@0 202 ops.len = len;
ian@0 203
ian@0 204 ret = mtd->read_oob(mtd, *ppos, &ops);
ian@0 205 retlen = ops.retlen;
ian@0 206 break;
ian@0 207 }
ian@0 208 default:
ian@0 209 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
ian@0 210 }
ian@0 211 /* Nand returns -EBADMSG on ecc errors, but it returns
ian@0 212 * the data. For our userspace tools it is important
ian@0 213 * to dump areas with ecc errors !
ian@0 214 * For kernel internal usage it also might return -EUCLEAN
ian@0 215 * to signal the caller that a bitflip has occured and has
ian@0 216 * been corrected by the ECC algorithm.
ian@0 217 * Userspace software which accesses NAND this way
ian@0 218 * must be aware of the fact that it deals with NAND
ian@0 219 */
ian@0 220 if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
ian@0 221 *ppos += retlen;
ian@0 222 if (copy_to_user(buf, kbuf, retlen)) {
ian@0 223 kfree(kbuf);
ian@0 224 return -EFAULT;
ian@0 225 }
ian@0 226 else
ian@0 227 total_retlen += retlen;
ian@0 228
ian@0 229 count -= retlen;
ian@0 230 buf += retlen;
ian@0 231 if (retlen == 0)
ian@0 232 count = 0;
ian@0 233 }
ian@0 234 else {
ian@0 235 kfree(kbuf);
ian@0 236 return ret;
ian@0 237 }
ian@0 238
ian@0 239 }
ian@0 240
ian@0 241 kfree(kbuf);
ian@0 242 return total_retlen;
ian@0 243 } /* mtd_read */
ian@0 244
ian@0 245 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
ian@0 246 {
ian@0 247 struct mtd_file_info *mfi = file->private_data;
ian@0 248 struct mtd_info *mtd = mfi->mtd;
ian@0 249 char *kbuf;
ian@0 250 size_t retlen;
ian@0 251 size_t total_retlen=0;
ian@0 252 int ret=0;
ian@0 253 int len;
ian@0 254
ian@0 255 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
ian@0 256
ian@0 257 if (*ppos == mtd->size)
ian@0 258 return -ENOSPC;
ian@0 259
ian@0 260 if (*ppos + count > mtd->size)
ian@0 261 count = mtd->size - *ppos;
ian@0 262
ian@0 263 if (!count)
ian@0 264 return 0;
ian@0 265
ian@0 266 if (count > MAX_KMALLOC_SIZE)
ian@0 267 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
ian@0 268 else
ian@0 269 kbuf=kmalloc(count, GFP_KERNEL);
ian@0 270
ian@0 271 if (!kbuf)
ian@0 272 return -ENOMEM;
ian@0 273
ian@0 274 while (count) {
ian@0 275
ian@0 276 if (count > MAX_KMALLOC_SIZE)
ian@0 277 len = MAX_KMALLOC_SIZE;
ian@0 278 else
ian@0 279 len = count;
ian@0 280
ian@0 281 if (copy_from_user(kbuf, buf, len)) {
ian@0 282 kfree(kbuf);
ian@0 283 return -EFAULT;
ian@0 284 }
ian@0 285
ian@0 286 switch (mfi->mode) {
ian@0 287 case MTD_MODE_OTP_FACTORY:
ian@0 288 ret = -EROFS;
ian@0 289 break;
ian@0 290 case MTD_MODE_OTP_USER:
ian@0 291 if (!mtd->write_user_prot_reg) {
ian@0 292 ret = -EOPNOTSUPP;
ian@0 293 break;
ian@0 294 }
ian@0 295 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
ian@0 296 break;
ian@0 297
ian@0 298 case MTD_MODE_RAW:
ian@0 299 {
ian@0 300 struct mtd_oob_ops ops;
ian@0 301
ian@0 302 ops.mode = MTD_OOB_RAW;
ian@0 303 ops.datbuf = kbuf;
ian@0 304 ops.oobbuf = NULL;
ian@0 305 ops.len = len;
ian@0 306
ian@0 307 ret = mtd->write_oob(mtd, *ppos, &ops);
ian@0 308 retlen = ops.retlen;
ian@0 309 break;
ian@0 310 }
ian@0 311
ian@0 312 default:
ian@0 313 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
ian@0 314 }
ian@0 315 if (!ret) {
ian@0 316 *ppos += retlen;
ian@0 317 total_retlen += retlen;
ian@0 318 count -= retlen;
ian@0 319 buf += retlen;
ian@0 320 }
ian@0 321 else {
ian@0 322 kfree(kbuf);
ian@0 323 return ret;
ian@0 324 }
ian@0 325 }
ian@0 326
ian@0 327 kfree(kbuf);
ian@0 328 return total_retlen;
ian@0 329 } /* mtd_write */
ian@0 330
ian@0 331 /*======================================================================
ian@0 332
ian@0 333 IOCTL calls for getting device parameters.
ian@0 334
ian@0 335 ======================================================================*/
ian@0 336 static void mtdchar_erase_callback (struct erase_info *instr)
ian@0 337 {
ian@0 338 wake_up((wait_queue_head_t *)instr->priv);
ian@0 339 }
ian@0 340
ian@0 341 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
ian@0 342 static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
ian@0 343 {
ian@0 344 struct mtd_info *mtd = mfi->mtd;
ian@0 345 int ret = 0;
ian@0 346
ian@0 347 switch (mode) {
ian@0 348 case MTD_OTP_FACTORY:
ian@0 349 if (!mtd->read_fact_prot_reg)
ian@0 350 ret = -EOPNOTSUPP;
ian@0 351 else
ian@0 352 mfi->mode = MTD_MODE_OTP_FACTORY;
ian@0 353 break;
ian@0 354 case MTD_OTP_USER:
ian@0 355 if (!mtd->read_fact_prot_reg)
ian@0 356 ret = -EOPNOTSUPP;
ian@0 357 else
ian@0 358 mfi->mode = MTD_MODE_OTP_USER;
ian@0 359 break;
ian@0 360 default:
ian@0 361 ret = -EINVAL;
ian@0 362 case MTD_OTP_OFF:
ian@0 363 break;
ian@0 364 }
ian@0 365 return ret;
ian@0 366 }
ian@0 367 #else
ian@0 368 # define otp_select_filemode(f,m) -EOPNOTSUPP
ian@0 369 #endif
ian@0 370
ian@0 371 static int mtd_ioctl(struct inode *inode, struct file *file,
ian@0 372 u_int cmd, u_long arg)
ian@0 373 {
ian@0 374 struct mtd_file_info *mfi = file->private_data;
ian@0 375 struct mtd_info *mtd = mfi->mtd;
ian@0 376 void __user *argp = (void __user *)arg;
ian@0 377 int ret = 0;
ian@0 378 u_long size;
ian@0 379 struct mtd_info_user info;
ian@0 380
ian@0 381 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
ian@0 382
ian@0 383 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
ian@0 384 if (cmd & IOC_IN) {
ian@0 385 if (!access_ok(VERIFY_READ, argp, size))
ian@0 386 return -EFAULT;
ian@0 387 }
ian@0 388 if (cmd & IOC_OUT) {
ian@0 389 if (!access_ok(VERIFY_WRITE, argp, size))
ian@0 390 return -EFAULT;
ian@0 391 }
ian@0 392
ian@0 393 switch (cmd) {
ian@0 394 case MEMGETREGIONCOUNT:
ian@0 395 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
ian@0 396 return -EFAULT;
ian@0 397 break;
ian@0 398
ian@0 399 case MEMGETREGIONINFO:
ian@0 400 {
ian@0 401 struct region_info_user ur;
ian@0 402
ian@0 403 if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
ian@0 404 return -EFAULT;
ian@0 405
ian@0 406 if (ur.regionindex >= mtd->numeraseregions)
ian@0 407 return -EINVAL;
ian@0 408 if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
ian@0 409 sizeof(struct mtd_erase_region_info)))
ian@0 410 return -EFAULT;
ian@0 411 break;
ian@0 412 }
ian@0 413
ian@0 414 case MEMGETINFO:
ian@0 415 info.type = mtd->type;
ian@0 416 info.flags = mtd->flags;
ian@0 417 info.size = mtd->size;
ian@0 418 info.erasesize = mtd->erasesize;
ian@0 419 info.writesize = mtd->writesize;
ian@0 420 info.oobsize = mtd->oobsize;
ian@0 421 info.ecctype = mtd->ecctype;
ian@0 422 info.eccsize = mtd->eccsize;
ian@0 423 if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
ian@0 424 return -EFAULT;
ian@0 425 break;
ian@0 426
ian@0 427 case MEMERASE:
ian@0 428 {
ian@0 429 struct erase_info *erase;
ian@0 430
ian@0 431 if(!(file->f_mode & 2))
ian@0 432 return -EPERM;
ian@0 433
ian@0 434 erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
ian@0 435 if (!erase)
ian@0 436 ret = -ENOMEM;
ian@0 437 else {
ian@0 438 wait_queue_head_t waitq;
ian@0 439 DECLARE_WAITQUEUE(wait, current);
ian@0 440
ian@0 441 init_waitqueue_head(&waitq);
ian@0 442
ian@0 443 memset (erase,0,sizeof(struct erase_info));
ian@0 444 if (copy_from_user(&erase->addr, argp,
ian@0 445 sizeof(struct erase_info_user))) {
ian@0 446 kfree(erase);
ian@0 447 return -EFAULT;
ian@0 448 }
ian@0 449 erase->mtd = mtd;
ian@0 450 erase->callback = mtdchar_erase_callback;
ian@0 451 erase->priv = (unsigned long)&waitq;
ian@0 452
ian@0 453 /*
ian@0 454 FIXME: Allow INTERRUPTIBLE. Which means
ian@0 455 not having the wait_queue head on the stack.
ian@0 456
ian@0 457 If the wq_head is on the stack, and we
ian@0 458 leave because we got interrupted, then the
ian@0 459 wq_head is no longer there when the
ian@0 460 callback routine tries to wake us up.
ian@0 461 */
ian@0 462 ret = mtd->erase(mtd, erase);
ian@0 463 if (!ret) {
ian@0 464 set_current_state(TASK_UNINTERRUPTIBLE);
ian@0 465 add_wait_queue(&waitq, &wait);
ian@0 466 if (erase->state != MTD_ERASE_DONE &&
ian@0 467 erase->state != MTD_ERASE_FAILED)
ian@0 468 schedule();
ian@0 469 remove_wait_queue(&waitq, &wait);
ian@0 470 set_current_state(TASK_RUNNING);
ian@0 471
ian@0 472 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
ian@0 473 }
ian@0 474 kfree(erase);
ian@0 475 }
ian@0 476 break;
ian@0 477 }
ian@0 478
ian@0 479 case MEMWRITEOOB:
ian@0 480 {
ian@0 481 struct mtd_oob_buf buf;
ian@0 482 struct mtd_oob_ops ops;
ian@0 483
ian@0 484 if(!(file->f_mode & 2))
ian@0 485 return -EPERM;
ian@0 486
ian@0 487 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
ian@0 488 return -EFAULT;
ian@0 489
ian@0 490 if (buf.length > 4096)
ian@0 491 return -EINVAL;
ian@0 492
ian@0 493 if (!mtd->write_oob)
ian@0 494 ret = -EOPNOTSUPP;
ian@0 495 else
ian@0 496 ret = access_ok(VERIFY_READ, buf.ptr,
ian@0 497 buf.length) ? 0 : EFAULT;
ian@0 498
ian@0 499 if (ret)
ian@0 500 return ret;
ian@0 501
ian@0 502 ops.len = buf.length;
ian@0 503 ops.ooblen = buf.length;
ian@0 504 ops.ooboffs = buf.start & (mtd->oobsize - 1);
ian@0 505 ops.datbuf = NULL;
ian@0 506 ops.mode = MTD_OOB_PLACE;
ian@0 507
ian@0 508 if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
ian@0 509 return -EINVAL;
ian@0 510
ian@0 511 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
ian@0 512 if (!ops.oobbuf)
ian@0 513 return -ENOMEM;
ian@0 514
ian@0 515 if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
ian@0 516 kfree(ops.oobbuf);
ian@0 517 return -EFAULT;
ian@0 518 }
ian@0 519
ian@0 520 buf.start &= ~(mtd->oobsize - 1);
ian@0 521 ret = mtd->write_oob(mtd, buf.start, &ops);
ian@0 522
ian@0 523 if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
ian@0 524 sizeof(uint32_t)))
ian@0 525 ret = -EFAULT;
ian@0 526
ian@0 527 kfree(ops.oobbuf);
ian@0 528 break;
ian@0 529
ian@0 530 }
ian@0 531
ian@0 532 case MEMREADOOB:
ian@0 533 {
ian@0 534 struct mtd_oob_buf buf;
ian@0 535 struct mtd_oob_ops ops;
ian@0 536
ian@0 537 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
ian@0 538 return -EFAULT;
ian@0 539
ian@0 540 if (buf.length > 4096)
ian@0 541 return -EINVAL;
ian@0 542
ian@0 543 if (!mtd->read_oob)
ian@0 544 ret = -EOPNOTSUPP;
ian@0 545 else
ian@0 546 ret = access_ok(VERIFY_WRITE, buf.ptr,
ian@0 547 buf.length) ? 0 : -EFAULT;
ian@0 548 if (ret)
ian@0 549 return ret;
ian@0 550
ian@0 551 ops.len = buf.length;
ian@0 552 ops.ooblen = buf.length;
ian@0 553 ops.ooboffs = buf.start & (mtd->oobsize - 1);
ian@0 554 ops.datbuf = NULL;
ian@0 555 ops.mode = MTD_OOB_PLACE;
ian@0 556
ian@0 557 if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
ian@0 558 return -EINVAL;
ian@0 559
ian@0 560 ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
ian@0 561 if (!ops.oobbuf)
ian@0 562 return -ENOMEM;
ian@0 563
ian@0 564 buf.start &= ~(mtd->oobsize - 1);
ian@0 565 ret = mtd->read_oob(mtd, buf.start, &ops);
ian@0 566
ian@0 567 if (put_user(ops.retlen, (uint32_t __user *)argp))
ian@0 568 ret = -EFAULT;
ian@0 569 else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
ian@0 570 ops.retlen))
ian@0 571 ret = -EFAULT;
ian@0 572
ian@0 573 kfree(ops.oobbuf);
ian@0 574 break;
ian@0 575 }
ian@0 576
ian@0 577 case MEMLOCK:
ian@0 578 {
ian@0 579 struct erase_info_user info;
ian@0 580
ian@0 581 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 582 return -EFAULT;
ian@0 583
ian@0 584 if (!mtd->lock)
ian@0 585 ret = -EOPNOTSUPP;
ian@0 586 else
ian@0 587 ret = mtd->lock(mtd, info.start, info.length);
ian@0 588 break;
ian@0 589 }
ian@0 590
ian@0 591 case MEMUNLOCK:
ian@0 592 {
ian@0 593 struct erase_info_user info;
ian@0 594
ian@0 595 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 596 return -EFAULT;
ian@0 597
ian@0 598 if (!mtd->unlock)
ian@0 599 ret = -EOPNOTSUPP;
ian@0 600 else
ian@0 601 ret = mtd->unlock(mtd, info.start, info.length);
ian@0 602 break;
ian@0 603 }
ian@0 604
ian@0 605 /* Legacy interface */
ian@0 606 case MEMGETOOBSEL:
ian@0 607 {
ian@0 608 struct nand_oobinfo oi;
ian@0 609
ian@0 610 if (!mtd->ecclayout)
ian@0 611 return -EOPNOTSUPP;
ian@0 612 if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
ian@0 613 return -EINVAL;
ian@0 614
ian@0 615 oi.useecc = MTD_NANDECC_AUTOPLACE;
ian@0 616 memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
ian@0 617 memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
ian@0 618 sizeof(oi.oobfree));
ian@0 619
ian@0 620 if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
ian@0 621 return -EFAULT;
ian@0 622 break;
ian@0 623 }
ian@0 624
ian@0 625 case MEMGETBADBLOCK:
ian@0 626 {
ian@0 627 loff_t offs;
ian@0 628
ian@0 629 if (copy_from_user(&offs, argp, sizeof(loff_t)))
ian@0 630 return -EFAULT;
ian@0 631 if (!mtd->block_isbad)
ian@0 632 ret = -EOPNOTSUPP;
ian@0 633 else
ian@0 634 return mtd->block_isbad(mtd, offs);
ian@0 635 break;
ian@0 636 }
ian@0 637
ian@0 638 case MEMSETBADBLOCK:
ian@0 639 {
ian@0 640 loff_t offs;
ian@0 641
ian@0 642 if (copy_from_user(&offs, argp, sizeof(loff_t)))
ian@0 643 return -EFAULT;
ian@0 644 if (!mtd->block_markbad)
ian@0 645 ret = -EOPNOTSUPP;
ian@0 646 else
ian@0 647 return mtd->block_markbad(mtd, offs);
ian@0 648 break;
ian@0 649 }
ian@0 650
ian@0 651 #if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
ian@0 652 case OTPSELECT:
ian@0 653 {
ian@0 654 int mode;
ian@0 655 if (copy_from_user(&mode, argp, sizeof(int)))
ian@0 656 return -EFAULT;
ian@0 657
ian@0 658 mfi->mode = MTD_MODE_NORMAL;
ian@0 659
ian@0 660 ret = otp_select_filemode(mfi, mode);
ian@0 661
ian@0 662 file->f_pos = 0;
ian@0 663 break;
ian@0 664 }
ian@0 665
ian@0 666 case OTPGETREGIONCOUNT:
ian@0 667 case OTPGETREGIONINFO:
ian@0 668 {
ian@0 669 struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
ian@0 670 if (!buf)
ian@0 671 return -ENOMEM;
ian@0 672 ret = -EOPNOTSUPP;
ian@0 673 switch (mfi->mode) {
ian@0 674 case MTD_MODE_OTP_FACTORY:
ian@0 675 if (mtd->get_fact_prot_info)
ian@0 676 ret = mtd->get_fact_prot_info(mtd, buf, 4096);
ian@0 677 break;
ian@0 678 case MTD_MODE_OTP_USER:
ian@0 679 if (mtd->get_user_prot_info)
ian@0 680 ret = mtd->get_user_prot_info(mtd, buf, 4096);
ian@0 681 break;
ian@0 682 default:
ian@0 683 break;
ian@0 684 }
ian@0 685 if (ret >= 0) {
ian@0 686 if (cmd == OTPGETREGIONCOUNT) {
ian@0 687 int nbr = ret / sizeof(struct otp_info);
ian@0 688 ret = copy_to_user(argp, &nbr, sizeof(int));
ian@0 689 } else
ian@0 690 ret = copy_to_user(argp, buf, ret);
ian@0 691 if (ret)
ian@0 692 ret = -EFAULT;
ian@0 693 }
ian@0 694 kfree(buf);
ian@0 695 break;
ian@0 696 }
ian@0 697
ian@0 698 case OTPLOCK:
ian@0 699 {
ian@0 700 struct otp_info info;
ian@0 701
ian@0 702 if (mfi->mode != MTD_MODE_OTP_USER)
ian@0 703 return -EINVAL;
ian@0 704 if (copy_from_user(&info, argp, sizeof(info)))
ian@0 705 return -EFAULT;
ian@0 706 if (!mtd->lock_user_prot_reg)
ian@0 707 return -EOPNOTSUPP;
ian@0 708 ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
ian@0 709 break;
ian@0 710 }
ian@0 711 #endif
ian@0 712
ian@0 713 case ECCGETLAYOUT:
ian@0 714 {
ian@0 715 if (!mtd->ecclayout)
ian@0 716 return -EOPNOTSUPP;
ian@0 717
ian@0 718 if (copy_to_user(argp, &mtd->ecclayout,
ian@0 719 sizeof(struct nand_ecclayout)))
ian@0 720 return -EFAULT;
ian@0 721 break;
ian@0 722 }
ian@0 723
ian@0 724 case ECCGETSTATS:
ian@0 725 {
ian@0 726 if (copy_to_user(argp, &mtd->ecc_stats,
ian@0 727 sizeof(struct mtd_ecc_stats)))
ian@0 728 return -EFAULT;
ian@0 729 break;
ian@0 730 }
ian@0 731
ian@0 732 case MTDFILEMODE:
ian@0 733 {
ian@0 734 mfi->mode = 0;
ian@0 735
ian@0 736 switch(arg) {
ian@0 737 case MTD_MODE_OTP_FACTORY:
ian@0 738 case MTD_MODE_OTP_USER:
ian@0 739 ret = otp_select_filemode(mfi, arg);
ian@0 740 break;
ian@0 741
ian@0 742 case MTD_MODE_RAW:
ian@0 743 if (!mtd->read_oob || !mtd->write_oob)
ian@0 744 return -EOPNOTSUPP;
ian@0 745 mfi->mode = arg;
ian@0 746
ian@0 747 case MTD_MODE_NORMAL:
ian@0 748 break;
ian@0 749 default:
ian@0 750 ret = -EINVAL;
ian@0 751 }
ian@0 752 file->f_pos = 0;
ian@0 753 break;
ian@0 754 }
ian@0 755
ian@0 756 default:
ian@0 757 ret = -ENOTTY;
ian@0 758 }
ian@0 759
ian@0 760 return ret;
ian@0 761 } /* memory_ioctl */
ian@0 762
ian@0 763 static struct file_operations mtd_fops = {
ian@0 764 .owner = THIS_MODULE,
ian@0 765 .llseek = mtd_lseek,
ian@0 766 .read = mtd_read,
ian@0 767 .write = mtd_write,
ian@0 768 .ioctl = mtd_ioctl,
ian@0 769 .open = mtd_open,
ian@0 770 .release = mtd_close,
ian@0 771 };
ian@0 772
ian@0 773 static int __init init_mtdchar(void)
ian@0 774 {
ian@0 775 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
ian@0 776 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
ian@0 777 MTD_CHAR_MAJOR);
ian@0 778 return -EAGAIN;
ian@0 779 }
ian@0 780
ian@0 781 mtd_class = class_create(THIS_MODULE, "mtd");
ian@0 782
ian@0 783 if (IS_ERR(mtd_class)) {
ian@0 784 printk(KERN_ERR "Error creating mtd class.\n");
ian@0 785 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
ian@0 786 return PTR_ERR(mtd_class);
ian@0 787 }
ian@0 788
ian@0 789 register_mtd_user(&notifier);
ian@0 790 return 0;
ian@0 791 }
ian@0 792
ian@0 793 static void __exit cleanup_mtdchar(void)
ian@0 794 {
ian@0 795 unregister_mtd_user(&notifier);
ian@0 796 class_destroy(mtd_class);
ian@0 797 unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
ian@0 798 }
ian@0 799
ian@0 800 module_init(init_mtdchar);
ian@0 801 module_exit(cleanup_mtdchar);
ian@0 802
ian@0 803
ian@0 804 MODULE_LICENSE("GPL");
ian@0 805 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
ian@0 806 MODULE_DESCRIPTION("Direct character-device access to MTD devices");