1 /* 2 * $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $ 3 * 4 * Character-device access to raw MTD devices. 5 * 6 */ 7 8 #include <linux/config.h> 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/mtd/mtd.h> 12 #include <linux/mtd/compatmac.h> 13 #include <linux/slab.h> 14 #include <linux/init.h> 15 #include <linux/fs.h> 16 #include <asm/uaccess.h> 17 18 #ifdef CONFIG_DEVFS_FS 19 #include <linux/devfs_fs_kernel.h> 20 21 static void mtd_notify_add(struct mtd_info* mtd) 22 { 23 if (!mtd) 24 return; 25 26 devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), 27 S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); 28 29 devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), 30 S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); 31 } 32 33 static void mtd_notify_remove(struct mtd_info* mtd) 34 { 35 if (!mtd) 36 return; 37 devfs_remove("mtd/%d", mtd->index); 38 devfs_remove("mtd/%dro", mtd->index); 39 } 40 41 static struct mtd_notifier notifier = { 42 .add = mtd_notify_add, 43 .remove = mtd_notify_remove, 44 }; 45 46 static inline void mtdchar_devfs_init(void) 47 { 48 devfs_mk_dir("mtd"); 49 register_mtd_user(¬ifier); 50 } 51 52 static inline void mtdchar_devfs_exit(void) 53 { 54 unregister_mtd_user(¬ifier); 55 devfs_remove("mtd"); 56 } 57 #else /* !DEVFS */ 58 #define mtdchar_devfs_init() do { } while(0) 59 #define mtdchar_devfs_exit() do { } while(0) 60 #endif 61 62 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) 63 { 64 struct mtd_info *mtd = file->private_data; 65 66 switch (orig) { 67 case 0: 68 /* SEEK_SET */ 69 file->f_pos = offset; 70 break; 71 case 1: 72 /* SEEK_CUR */ 73 file->f_pos += offset; 74 break; 75 case 2: 76 /* SEEK_END */ 77 file->f_pos =mtd->size + offset; 78 break; 79 default: 80 return -EINVAL; 81 } 82 83 if (file->f_pos < 0) 84 file->f_pos = 0; 85 else if (file->f_pos >= mtd->size) 86 file->f_pos = mtd->size - 1; 87 88 return file->f_pos; 89 } 90 91 92 93 static int mtd_open(struct inode *inode, struct file *file) 94 { 95 int minor = iminor(inode); 96 int devnum = minor >> 1; 97 struct mtd_info *mtd; 98 99 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); 100 101 if (devnum >= MAX_MTD_DEVICES) 102 return -ENODEV; 103 104 /* You can't open the RO devices RW */ 105 if ((file->f_mode & 2) && (minor & 1)) 106 return -EACCES; 107 108 mtd = get_mtd_device(NULL, devnum); 109 110 if (!mtd) 111 return -ENODEV; 112 113 if (MTD_ABSENT == mtd->type) { 114 put_mtd_device(mtd); 115 return -ENODEV; 116 } 117 118 file->private_data = mtd; 119 120 /* You can't open it RW if it's not a writeable device */ 121 if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) { 122 put_mtd_device(mtd); 123 return -EACCES; 124 } 125 126 return 0; 127 } /* mtd_open */ 128 129 /*====================================================================*/ 130 131 static int mtd_close(struct inode *inode, struct file *file) 132 { 133 struct mtd_info *mtd; 134 135 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); 136 137 mtd = file->private_data; 138 139 if (mtd->sync) 140 mtd->sync(mtd); 141 142 put_mtd_device(mtd); 143 144 return 0; 145 } /* mtd_close */ 146 147 /* FIXME: This _really_ needs to die. In 2.5, we should lock the 148 userspace buffer down and use it directly with readv/writev. 149 */ 150 #define MAX_KMALLOC_SIZE 0x20000 151 152 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) 153 { 154 struct mtd_info *mtd = file->private_data; 155 size_t retlen=0; 156 size_t total_retlen=0; 157 int ret=0; 158 int len; 159 char *kbuf; 160 161 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); 162 163 if (*ppos + count > mtd->size) 164 count = mtd->size - *ppos; 165 166 if (!count) 167 return 0; 168 169 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers 170 and pass them directly to the MTD functions */ 171 while (count) { 172 if (count > MAX_KMALLOC_SIZE) 173 len = MAX_KMALLOC_SIZE; 174 else 175 len = count; 176 177 kbuf=kmalloc(len,GFP_KERNEL); 178 if (!kbuf) 179 return -ENOMEM; 180 181 ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); 182 /* Nand returns -EBADMSG on ecc errors, but it returns 183 * the data. For our userspace tools it is important 184 * to dump areas with ecc errors ! 185 * Userspace software which accesses NAND this way 186 * must be aware of the fact that it deals with NAND 187 */ 188 if (!ret || (ret == -EBADMSG)) { 189 *ppos += retlen; 190 if (copy_to_user(buf, kbuf, retlen)) { 191 kfree(kbuf); 192 return -EFAULT; 193 } 194 else 195 total_retlen += retlen; 196 197 count -= retlen; 198 buf += retlen; 199 } 200 else { 201 kfree(kbuf); 202 return ret; 203 } 204 205 kfree(kbuf); 206 } 207 208 return total_retlen; 209 } /* mtd_read */ 210 211 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) 212 { 213 struct mtd_info *mtd = file->private_data; 214 char *kbuf; 215 size_t retlen; 216 size_t total_retlen=0; 217 int ret=0; 218 int len; 219 220 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); 221 222 if (*ppos == mtd->size) 223 return -ENOSPC; 224 225 if (*ppos + count > mtd->size) 226 count = mtd->size - *ppos; 227 228 if (!count) 229 return 0; 230 231 while (count) { 232 if (count > MAX_KMALLOC_SIZE) 233 len = MAX_KMALLOC_SIZE; 234 else 235 len = count; 236 237 kbuf=kmalloc(len,GFP_KERNEL); 238 if (!kbuf) { 239 printk("kmalloc is null\n"); 240 return -ENOMEM; 241 } 242 243 if (copy_from_user(kbuf, buf, len)) { 244 kfree(kbuf); 245 return -EFAULT; 246 } 247 248 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 249 if (!ret) { 250 *ppos += retlen; 251 total_retlen += retlen; 252 count -= retlen; 253 buf += retlen; 254 } 255 else { 256 kfree(kbuf); 257 return ret; 258 } 259 260 kfree(kbuf); 261 } 262 263 return total_retlen; 264 } /* mtd_write */ 265 266 /*====================================================================== 267 268 IOCTL calls for getting device parameters. 269 270 ======================================================================*/ 271 static void mtdchar_erase_callback (struct erase_info *instr) 272 { 273 wake_up((wait_queue_head_t *)instr->priv); 274 } 275 276 static int mtd_ioctl(struct inode *inode, struct file *file, 277 u_int cmd, u_long arg) 278 { 279 struct mtd_info *mtd = file->private_data; 280 void __user *argp = (void __user *)arg; 281 int ret = 0; 282 u_long size; 283 284 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); 285 286 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; 287 if (cmd & IOC_IN) { 288 if (!access_ok(VERIFY_READ, argp, size)) 289 return -EFAULT; 290 } 291 if (cmd & IOC_OUT) { 292 if (!access_ok(VERIFY_WRITE, argp, size)) 293 return -EFAULT; 294 } 295 296 switch (cmd) { 297 case MEMGETREGIONCOUNT: 298 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int))) 299 return -EFAULT; 300 break; 301 302 case MEMGETREGIONINFO: 303 { 304 struct region_info_user ur; 305 306 if (copy_from_user(&ur, argp, sizeof(struct region_info_user))) 307 return -EFAULT; 308 309 if (ur.regionindex >= mtd->numeraseregions) 310 return -EINVAL; 311 if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]), 312 sizeof(struct mtd_erase_region_info))) 313 return -EFAULT; 314 break; 315 } 316 317 case MEMGETINFO: 318 if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user))) 319 return -EFAULT; 320 break; 321 322 case MEMERASE: 323 { 324 struct erase_info *erase; 325 326 if(!(file->f_mode & 2)) 327 return -EPERM; 328 329 erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); 330 if (!erase) 331 ret = -ENOMEM; 332 else { 333 wait_queue_head_t waitq; 334 DECLARE_WAITQUEUE(wait, current); 335 336 init_waitqueue_head(&waitq); 337 338 memset (erase,0,sizeof(struct erase_info)); 339 if (copy_from_user(&erase->addr, argp, 340 sizeof(struct erase_info_user))) { 341 kfree(erase); 342 return -EFAULT; 343 } 344 erase->mtd = mtd; 345 erase->callback = mtdchar_erase_callback; 346 erase->priv = (unsigned long)&waitq; 347 348 /* 349 FIXME: Allow INTERRUPTIBLE. Which means 350 not having the wait_queue head on the stack. 351 352 If the wq_head is on the stack, and we 353 leave because we got interrupted, then the 354 wq_head is no longer there when the 355 callback routine tries to wake us up. 356 */ 357 ret = mtd->erase(mtd, erase); 358 if (!ret) { 359 set_current_state(TASK_UNINTERRUPTIBLE); 360 add_wait_queue(&waitq, &wait); 361 if (erase->state != MTD_ERASE_DONE && 362 erase->state != MTD_ERASE_FAILED) 363 schedule(); 364 remove_wait_queue(&waitq, &wait); 365 set_current_state(TASK_RUNNING); 366 367 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0; 368 } 369 kfree(erase); 370 } 371 break; 372 } 373 374 case MEMWRITEOOB: 375 { 376 struct mtd_oob_buf buf; 377 void *databuf; 378 ssize_t retlen; 379 380 if(!(file->f_mode & 2)) 381 return -EPERM; 382 383 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) 384 return -EFAULT; 385 386 if (buf.length > 0x4096) 387 return -EINVAL; 388 389 if (!mtd->write_oob) 390 ret = -EOPNOTSUPP; 391 else 392 ret = access_ok(VERIFY_READ, buf.ptr, 393 buf.length) ? 0 : EFAULT; 394 395 if (ret) 396 return ret; 397 398 databuf = kmalloc(buf.length, GFP_KERNEL); 399 if (!databuf) 400 return -ENOMEM; 401 402 if (copy_from_user(databuf, buf.ptr, buf.length)) { 403 kfree(databuf); 404 return -EFAULT; 405 } 406 407 ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); 408 409 if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t))) 410 ret = -EFAULT; 411 412 kfree(databuf); 413 break; 414 415 } 416 417 case MEMREADOOB: 418 { 419 struct mtd_oob_buf buf; 420 void *databuf; 421 ssize_t retlen; 422 423 if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) 424 return -EFAULT; 425 426 if (buf.length > 0x4096) 427 return -EINVAL; 428 429 if (!mtd->read_oob) 430 ret = -EOPNOTSUPP; 431 else 432 ret = access_ok(VERIFY_WRITE, buf.ptr, 433 buf.length) ? 0 : -EFAULT; 434 435 if (ret) 436 return ret; 437 438 databuf = kmalloc(buf.length, GFP_KERNEL); 439 if (!databuf) 440 return -ENOMEM; 441 442 ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); 443 444 if (put_user(retlen, (uint32_t __user *)argp)) 445 ret = -EFAULT; 446 else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) 447 ret = -EFAULT; 448 449 kfree(databuf); 450 break; 451 } 452 453 case MEMLOCK: 454 { 455 struct erase_info_user info; 456 457 if (copy_from_user(&info, argp, sizeof(info))) 458 return -EFAULT; 459 460 if (!mtd->lock) 461 ret = -EOPNOTSUPP; 462 else 463 ret = mtd->lock(mtd, info.start, info.length); 464 break; 465 } 466 467 case MEMUNLOCK: 468 { 469 struct erase_info_user info; 470 471 if (copy_from_user(&info, argp, sizeof(info))) 472 return -EFAULT; 473 474 if (!mtd->unlock) 475 ret = -EOPNOTSUPP; 476 else 477 ret = mtd->unlock(mtd, info.start, info.length); 478 break; 479 } 480 481 case MEMSETOOBSEL: 482 { 483 if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo))) 484 return -EFAULT; 485 break; 486 } 487 488 case MEMGETOOBSEL: 489 { 490 if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo))) 491 return -EFAULT; 492 break; 493 } 494 495 case MEMGETBADBLOCK: 496 { 497 loff_t offs; 498 499 if (copy_from_user(&offs, argp, sizeof(loff_t))) 500 return -EFAULT; 501 if (!mtd->block_isbad) 502 ret = -EOPNOTSUPP; 503 else 504 return mtd->block_isbad(mtd, offs); 505 break; 506 } 507 508 case MEMSETBADBLOCK: 509 { 510 loff_t offs; 511 512 if (copy_from_user(&offs, argp, sizeof(loff_t))) 513 return -EFAULT; 514 if (!mtd->block_markbad) 515 ret = -EOPNOTSUPP; 516 else 517 return mtd->block_markbad(mtd, offs); 518 break; 519 } 520 521 default: 522 ret = -ENOTTY; 523 } 524 525 return ret; 526 } /* memory_ioctl */ 527 528 static struct file_operations mtd_fops = { 529 .owner = THIS_MODULE, 530 .llseek = mtd_lseek, 531 .read = mtd_read, 532 .write = mtd_write, 533 .ioctl = mtd_ioctl, 534 .open = mtd_open, 535 .release = mtd_close, 536 }; 537 538 static int __init init_mtdchar(void) 539 { 540 if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { 541 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", 542 MTD_CHAR_MAJOR); 543 return -EAGAIN; 544 } 545 546 mtdchar_devfs_init(); 547 return 0; 548 } 549 550 static void __exit cleanup_mtdchar(void) 551 { 552 mtdchar_devfs_exit(); 553 unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); 554 } 555 556 module_init(init_mtdchar); 557 module_exit(cleanup_mtdchar); 558 559 560 MODULE_LICENSE("GPL"); 561 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 562 MODULE_DESCRIPTION("Direct character-device access to MTD devices"); 563